diff --git a/AGENTS.md b/AGENTS.md
index fd68b560..ff2aefec 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -4,7 +4,7 @@
## Specs
-The specs in `specs/` are the source of truth. Start with `specs/README.md` (reading order, conventions), then `specs/01-architecture.md` (layout, package rules, how the apps are served). App specs: `specs/wonky_write/`, `specs/wonky_sheets/`, `specs/wonky_drive/` and `specs/wonky_slides/` (v2 decisions: `specs/v2-review.md`). Cross-app features: `specs/16-bugs-and-improvements.md` (Bugs & improvements board, M12); the RevLog sync of Sheets and Slides: `specs/17-revlog-sync.md`. When implementation reveals a better approach, update the spec in the same change.
+The specs in `specs/` are the source of truth. Start with `specs/README.md` (reading order, conventions), then `specs/01-architecture.md` (layout, package rules, how the apps are served). App specs: `specs/wonky_write/`, `specs/wonky_sheets/`, `specs/wonky_drive/` and `specs/wonky_slides/` (v2 decisions: `specs/v2-review.md`). Cross-app features: `specs/16-bugs-and-improvements.md` (Bugs & improvements board, M12); the RevLog sync of Sheets and Slides: `specs/17-revlog-sync.md`. Blog submissions from Wonky Write (M14, built): `specs/18-publications-and-submissions.md`; the guide for blog owners is `docs/blog-integration.md`. When implementation reveals a better approach, update the spec in the same change.
Key rules from the specs: every user-visible action is a command and every state change is undoable (`specs/08-commands-and-undo.md`); single window, never open new tabs or windows (`specs/03-app-shell-and-navigation.md`); UI is built with `shad` and custom widgets in `wonky_ui` (`specs/02-design-system.md`); everything works in light and dark.
diff --git a/TODO.md b/TODO.md
index ef1f83dd..3a38842e 100644
--- a/TODO.md
+++ b/TODO.md
@@ -136,6 +136,17 @@ committed and verified).
- PDF exports (client and server) cover Latin, Greek and Cyrillic with the bundled TTFs; CJK, Arabic and emoji fall back to placeholders until the fallback fonts are added.
- WYSIWYG Markdown composer (`wonky_rich_editor`, specs/16 §7.3): ☐ a code block's language can only be set when typing the fence; ☐ browser check of IME typing (the first keys after a click, fixed in the widget, see §7.3), paste, copy as Markdown, the spelling menu and the link dialog in the release builds; ☐ the composer's spelling language is the device locale (no per-field language).
+## Publications & submissions (M14) — open items
+
+Spec: `specs/18-publications-and-submissions.md` §13; guide: `docs/blog-integration.md`.
+
+- [ ] Light/dark goldens of the consent dialog, submit dialog, status chip and Settings ›
+ Publications.
+- [ ] E2E journey J-P1 (link → sign up → write → submit → changes requested → resubmit →
+ published) against a fake blog receiver, and a walkthrough against the live server.
+- [ ] Moderators who sign up later get access only with the next submit or status change; grant
+ at sign-up instead.
+
## RevLog sync — open items
Sheets and Slides sync through RevLog (`specs/17-revlog-sync.md`); the CRDT engine, the sync
diff --git a/docs/blog-integration.md b/docs/blog-integration.md
new file mode 100644
index 00000000..312983f8
--- /dev/null
+++ b/docs/blog-integration.md
@@ -0,0 +1,507 @@
+# Take stories from Wonky Write on your blog
+
+This guide is for blog owners and their developers. It shows how to let people write a story
+for your blog in [Wonky Write](https://wonky.work/write/) and send it to your review queue with
+one button.
+
+How it works:
+
+1. Your blog links to Wonky Write with a **Write for us** button.
+2. The contributor signs in (or signs up), agrees to send their story to your blog, and writes in
+ their own Wonky document. Co-authors, comments, suggestions and offline editing all work as
+ usual.
+3. The contributor presses **Submit**. Wonky sends the story to your server as a signed webhook,
+ and you put it in your review queue.
+4. Your moderators read the story, and can comment and suggest edits in the Wonky document.
+5. Your moderation tool tells Wonky the outcome: *changes requested*, *declined* or *published*,
+ with an optional message. The contributor sees it in the document and gets a notification.
+ After changes, they press **Resubmit** and the story comes back to your queue.
+
+```
+ Your blog Wonky Contributor
+ ───────── ───── ───────────
+ "Write for us" link ───────────▶ /write/new?publication=pub_… ◀──── opens link, signs in
+ new document for the story ◀──── writes, presses Submit
+ webhook receiver ◀── POST ───── submission.submitted (signed)
+ review queue
+ moderator opens the document ───▶ comments / suggestions ────▶ notified
+ status API ── POST ────────────▶ "changesRequested" + message ────▶ notified, edits, Resubmit
+ webhook receiver ◀── POST ───── submission.submitted (sequence 2)
+ status API ── POST ────────────▶ "published" + post URL ────▶ "Published · View post"
+```
+
+What you need:
+
+- A Wonky account (free, at [wonky.work](https://wonky.work)).
+- An HTTPS endpoint on your server that can receive webhooks (port 443, a public address).
+- A place on your server to keep two secrets: the **signing secret** and the **API key**. Never
+ put them in your website's frontend code.
+- Your moderators' Wonky accounts, if they should read and comment on stories in Wonky.
+
+The full design is in [specs/18-publications-and-submissions.md](../specs/18-publications-and-submissions.md).
+
+---
+
+## 1. Create a publication
+
+In Wonky, open **Settings › Publications** (any app: account menu › *Wonky Work settings*) and
+choose **New publication**.
+
+| Setting | What to enter |
+|---------|---------------|
+| Name | What contributors see, e.g. *Serverpod blog* (up to 60 characters) |
+| Site URL | Your blog's address, origin only: `https://blog.example.com` |
+| Webhook URL | Where Wonky sends submissions: `https://blog.example.com/api/wonky/webhook`. It must be `https` on port 443 and reachable from the internet. Wonky doesn't follow redirects |
+| Terms URL | Optional. Your contributor terms. When set, contributors must agree to them before they start |
+| Template | Optional. One of your own Wonky Write documents. New stories start as a copy of it (without its comments), so you can include guidelines and a suggested structure |
+| Submission fields | The title is always asked for. Turn on *Summary*, *Tags* (optionally from a fixed list), *Cover image*, *Author bio* and *Note to the moderators*, each optional or required |
+| Moderators | The email addresses of your moderators' Wonky accounts (up to 20). A moderator without an account gets access once they sign up with that email |
+
+When you create the publication, Wonky shows the **signing secret** (`whsec_…`) **once**. Copy
+it into your server's secret store now. Then choose **Create API key** and store the key
+(`wpk_…`) as well; it's also shown only once.
+
+The publication page also shows the **publication id** (`pub_…`) and the **contributor link**.
+Neither is a secret.
+
+## 2. Verify your site
+
+Verification shows contributors a ✓ next to your site's name, so they know the publication is
+really yours. Without it, Wonky shows your webhook's host instead of your site and a warning.
+
+Serve this file at `https://blog.example.com/.well-known/wonky-publications.json`:
+
+```json
+{ "publications": ["pub_7Hk2xQ9mR4tLwP0aZc3VbN"] }
+```
+
+List every publication id that belongs to the site. Then choose **Verify** on the publication
+page. Changing the site URL later removes the verification; verify again.
+
+## 3. Add the "Write for us" link
+
+```html
+Write for us
+```
+
+You can add `&ref=` (up to 64 characters: letters, digits, `-` and `_`) to tell
+submissions apart, for example by a call for posts: `…&ref=cfp-2026`. Wonky passes `ref` back
+in every webhook. Treat it as a hint from the link, not as proof of anything.
+
+The link always works the same way, so it can sit on a static page. A contributor who already
+has an unsubmitted story for your blog is offered to continue it.
+
+## 4. Receive webhooks
+
+Wonky sends `POST` requests with a JSON body to your webhook URL, signed per the
+[Standard Webhooks](https://www.standardwebhooks.com/) specification. There are libraries for
+most languages (`standardwebhooks` on npm and PyPI, and others); you can also check the
+signature yourself in a few lines (below).
+
+### 4.1 Headers
+
+| Header | Value |
+|--------|-------|
+| `webhook-id` | `msg_…`, unique per event. A retry of the same event uses the **same id** |
+| `webhook-timestamp` | Unix time in seconds when this attempt was sent |
+| `webhook-signature` | `v1,`. During a secret rotation there are two, separated by a space; accept the request if either matches |
+| `content-type` | `application/json` |
+
+### 4.2 Check the signature
+
+1. Take the secret without its `whsec_` prefix and base64-decode it: that's the HMAC key.
+2. Build the signed content: `..`. Use the raw
+ body exactly as received, before any JSON parsing.
+3. Compute HMAC-SHA256 with the key, base64-encode it, and compare it with each `v1,` signature
+ in the header using a constant-time comparison.
+4. Reject the request if the timestamp is more than 5 minutes away from your clock.
+5. Remember the `webhook-id`s you've handled (a day is enough) and ignore repeats.
+
+**Node.js**
+
+```js
+import crypto from "node:crypto";
+
+export function verifyWonkyWebhook(rawBody, headers, secret) {
+ const id = headers["webhook-id"];
+ const timestamp = headers["webhook-timestamp"];
+ const signatures = (headers["webhook-signature"] ?? "").split(" ");
+ if (!id || !timestamp) return false;
+ if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
+ const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
+ const expected = crypto
+ .createHmac("sha256", key)
+ .update(`${id}.${timestamp}.${rawBody}`)
+ .digest();
+ return signatures.some((s) => {
+ const [version, value] = s.split(",");
+ if (version !== "v1" || !value) return false;
+ const given = Buffer.from(value, "base64");
+ return given.length === expected.length && crypto.timingSafeEqual(given, expected);
+ });
+}
+```
+
+**Python**
+
+```python
+import base64, hashlib, hmac, time
+
+def verify_wonky_webhook(raw_body: bytes, headers, secret: str) -> bool:
+ msg_id = headers.get("webhook-id")
+ timestamp = headers.get("webhook-timestamp")
+ if not msg_id or not timestamp or abs(time.time() - int(timestamp)) > 300:
+ return False
+ key = base64.b64decode(secret.removeprefix("whsec_"))
+ signed = f"{msg_id}.{timestamp}.".encode() + raw_body
+ expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
+ return any(
+ s.startswith("v1,") and hmac.compare_digest(s[3:], expected)
+ for s in headers.get("webhook-signature", "").split(" ")
+ )
+```
+
+**Dart**
+
+```dart
+import 'dart:convert';
+import 'package:crypto/crypto.dart';
+
+bool verifyWonkyWebhook(String rawBody, Map headers, String secret) {
+ final id = headers['webhook-id'];
+ final timestamp = int.tryParse(headers['webhook-timestamp'] ?? '');
+ if (id == null || timestamp == null) return false;
+ final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
+ if ((now - timestamp).abs() > 300) return false;
+ final key = base64.decode(secret.substring('whsec_'.length));
+ final expected = Hmac(sha256, key).convert(utf8.encode('$id.$timestamp.$rawBody')).bytes;
+ for (final s in (headers['webhook-signature'] ?? '').split(' ')) {
+ if (!s.startsWith('v1,')) continue;
+ final given = base64.decode(s.substring(3));
+ if (given.length != expected.length) continue;
+ var diff = 0;
+ for (var i = 0; i < given.length; i++) {
+ diff |= given[i] ^ expected[i];
+ }
+ if (diff == 0) return true;
+ }
+ return false;
+}
+```
+
+**Test vector.** With the secret `whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw`, the id
+`msg_2KWPBgLlAfxdpx2AI54pPJ85f4W`, the timestamp `1674087231` and this body (one line, no
+trailing newline):
+
+```
+{"type":"publication.ping","createdAt":"2023-01-19T00:13:51Z","publicationId":"pub_7Hk2xQ9mR4tLwP0aZc3VbN"}
+```
+
+the signature is `v1,4+meITVYE+3sv8v+3Jl0/CXDGdczBRF7SOHoZxKwobQ=` (skip the timestamp check
+when you test with it).
+
+### 4.3 Answer quickly
+
+Answer with any `2xx` status within **10 seconds**; the answer's body is ignored. Store the event
+and do slow work (downloading images, notifying moderators) afterwards.
+
+Anything else (an error status, a timeout, a redirect, a connection error) counts as not
+delivered. Wonky retries with the same `webhook-id` after about 1 minute, 5 minutes, 30 minutes,
+2 hours, 6 hours and 12 hours, then gives up. While it retries, the contributor sees
+*Submitting…*; if every attempt fails, they're told that your blog couldn't receive the story.
+You can see each attempt, and resend it, under **Deliveries** on the publication page.
+
+Events can arrive more than once and out of order. Use `webhook-id` to ignore repeats and
+`sequence` (below) to ignore an older submit that arrives after a newer one.
+
+### 4.4 Events
+
+Every body has these fields:
+
+```json
+{
+ "type": "submission.submitted",
+ "createdAt": "2026-10-20T09:14:03Z",
+ "publicationId": "pub_7Hk2xQ9mR4tLwP0aZc3VbN"
+}
+```
+
+| `type` | When | Other fields |
+|--------|------|--------------|
+| `publication.ping` | You choose *Send test*, or save a new webhook URL | — |
+| `submission.submitted` | Every Submit, Resubmit and Submit update | `submission`, `sequence`, `revision`, `fields`, `markdown`, `assets`, `submittedBy` |
+| `submission.withdrawn` | The contributor withdraws the story, or moves the document to the trash | `submission`, `reason`: `author` or `trashed` |
+| `submission.disconnected` | The contributor disconnects the document from your blog. No more updates will come for this submission | `submission` |
+| `comment.created` | Someone replies in the document's thread of your blog's messages (§6.2) | `submission`, `comment` |
+
+Ignore types you don't know; new ones may be added.
+
+### 4.5 `submission.submitted`
+
+```json
+{
+ "type": "submission.submitted",
+ "createdAt": "2026-10-20T09:14:03Z",
+ "publicationId": "pub_7Hk2xQ9mR4tLwP0aZc3VbN",
+ "submission": {
+ "id": "sub_0192a3b4-5c6d-7e8f-9012-3456789abcde",
+ "ref": "cfp-2026",
+ "status": "inReview",
+ "documentUrl": "https://wonky.work/write/d/0192a3b4-0000-7000-8000-000000000001"
+ },
+ "sequence": 2,
+ "revision": 418,
+ "fields": {
+ "title": "How we sped up our CI three times",
+ "summary": "A short look at caching, sharding and one weird flag.",
+ "tags": ["dart", "ci"],
+ "coverImage": "ast_0192a3b4-1111-7000-8000-000000000002",
+ "authorBio": null,
+ "note": "Fixed the code samples, thanks!"
+ },
+ "markdown": "# How we sped up our CI three times\n\n…\n\n\n",
+ "assets": [
+ {
+ "id": "ast_0192a3b4-1111-7000-8000-000000000002",
+ "name": "build-times.png",
+ "type": "image/png",
+ "bytes": 48213,
+ "url": "https://wonky.work/api/pub/v1/assets/ast_0192a3b4-1111-7000-8000-000000000002"
+ }
+ ],
+ "submittedBy": { "name": "Anna Berg", "wonkyUserId": "0191f0e2-aaaa-7000-8000-000000000003" }
+}
+```
+
+- `submission.id` is stable for the story: use it as the key of your queue entry and update the
+ entry on every new submit.
+- `sequence` counts the submits of this submission (1, 2, …). Ignore a webhook whose `sequence`
+ is lower than the one you have.
+- `revision` is the document revision that was sent. When you publish, publish the content of the
+ submit you reviewed; the contributor may keep editing in the meantime.
+- `fields` holds the fields you turned on; fields you didn't turn on are `null` (`tags` is then
+ `[]`). `coverImage` is the id of one of the `assets`.
+- `markdown` is CommonMark with GitHub tables and task lists. Open suggestions in the document are
+ sent as the original text; comments are never included. There is no front matter: the title
+ and the other fields are in `fields` (the Markdown usually starts with the title as a heading,
+ because that's how people write).
+- Images uploaded to Wonky appear as asset URLs (§5). Images that were already on the web keep
+ their own `https://` URLs.
+- `submittedBy.name` is the display name of the person who pressed Submit, which may be a
+ co-author. Ask for the byline in a field if you need one. Wonky never sends email addresses.
+ `wonkyUserId` is stable per Wonky account; use it to block a user (§7.6).
+- `documentUrl` opens the story in Wonky. Your moderators can open it once Wonky has granted them
+ access (§6.1).
+
+### 4.6 `comment.created`
+
+```json
+{
+ "type": "comment.created",
+ "createdAt": "2026-10-21T08:02:11Z",
+ "publicationId": "pub_7Hk2xQ9mR4tLwP0aZc3VbN",
+ "submission": { "id": "sub_…", "ref": "cfp-2026", "status": "changesRequested",
+ "documentUrl": "https://wonky.work/write/d/…" },
+ "comment": {
+ "id": "0192a3b4-2222-7000-8000-000000000004",
+ "body": "Done — I split the second section in two.",
+ "author": { "name": "Anna Berg", "wonkyUserId": "0191f0e2-…", "isModerator": false }
+ }
+}
+```
+
+`body` is Markdown (bold, italic, code and links).
+
+## 5. Download the images
+
+Asset URLs need your API key:
+
+```bash
+curl -H "Authorization: Bearer $WONKY_API_KEY" -o build-times.png \
+ https://wonky.work/api/pub/v1/assets/ast_0192a3b4-1111-7000-8000-000000000002
+```
+
+Download them right after you receive the webhook and host them yourself. An asset stays
+available while the image exists in Wonky, but the contributor can delete it later. Only images
+that were part of one of your submissions can be downloaded.
+
+## 6. Moderate
+
+### 6.1 In Wonky
+
+When a story is submitted for the first time, each moderator gets **comment access** to the
+document. They can read it, leave comments and suggest edits; they can't change the text
+directly. The access ends when the story is withdrawn, declined or disconnected. Stories you have
+published keep it, so you can review updates the same way. The contributor (or a co-author) can
+also remove a moderator's access in the document's *Share* dialog; it comes back with the next
+submit. A moderator who creates their Wonky account later gets access with the next submit or
+status change.
+
+Moderators don't get an email when they receive access. Link to `documentUrl` from your review
+queue (*Open in Wonky*).
+
+### 6.2 Messages back to the contributor
+
+Your status calls and messages (§7.4, §7.5) appear in the document in one comment thread under
+your publication's name, and every editor of the document is notified. When someone replies in
+that thread, you receive `comment.created`. Comments elsewhere in the document stay in Wonky, but
+your moderators can read them there.
+
+### 6.3 Status flow
+
+```
+ draft ──Submit──▶ inReview ──you──▶ changesRequested ──Resubmit──▶ inReview
+ │
+ ├──you──▶ declined (final for this submission)
+ └──you──▶ published ──Submit update──▶ inReview
+ inReview / changesRequested ──contributor withdraws──▶ withdrawn ──Submit──▶ inReview
+```
+
+Wonky sets `inReview` itself when you accept a `submission.submitted` webhook. You set the rest:
+
+| From | You may set |
+|------|-------------|
+| `inReview` | `inReview`, `changesRequested`, `declined`, `published` |
+| `changesRequested` | `inReview`, `changesRequested`, `declined`, `published` |
+| `published` | `published` (for example to change the post URL) |
+| `draft`, `declined`, `withdrawn` | nothing |
+
+## 7. API reference
+
+Base URL: `https://wonky.work/api/pub/v1/`
+
+- **Authentication**: `Authorization: Bearer wpk_…` on every request. A key only reaches its own
+ publication's submissions.
+- **Bodies**: JSON, at most 1 MB.
+- **Rate limit**: 120 requests per minute per key. Over it you get `429` with a `retry-after`
+ header in seconds.
+- **Errors**: a status code and `{ "error": "", "message": "" }`.
+
+| Status | `error` | Meaning |
+|--------|---------|---------|
+| 400 | `invalidArgument` | A field is missing or wrong; `message` says which |
+| 401 | `unauthorized` | No key, a malformed key, or a key that was rotated or deleted |
+| 404 | `notFound` | No such submission or asset for this publication |
+| 409 | `invalidTransition` | The status change isn't allowed from the current status (§6.3) |
+| 410 | `gone` | The submission was withdrawn or disconnected |
+| 413 | `tooLarge` | The body is larger than 1 MB |
+| 429 | `rateLimited` | Too many requests |
+
+### 7.1 List submissions
+
+`GET submissions?status=inReview&after=&limit=50`
+
+`status` is optional (`draft`, `inReview`, `changesRequested`, `declined`, `published`,
+`withdrawn`); `limit` is 1–100 (default 50). The newest change comes first. Pass the answer's
+`next` as `after` to get the next page; `next` is `null` on the last page.
+
+```json
+{
+ "submissions": [
+ {
+ "id": "sub_…", "ref": "cfp-2026", "status": "inReview",
+ "documentUrl": "https://wonky.work/write/d/…",
+ "title": "How we sped up our CI three times",
+ "sequence": 2, "revision": 418,
+ "submittedAt": "2026-10-20T09:14:03Z", "updatedAt": "2026-10-20T09:14:03Z",
+ "publicUrl": null
+ }
+ ],
+ "next": null
+}
+```
+
+Use it to catch up after downtime. It isn't needed in normal operation.
+
+### 7.2 Get a submission
+
+`GET submissions/{id}` returns one entry as in the list, plus `fields`.
+
+### 7.3 Get a submitted version
+
+`GET submissions/{id}/content?sequence=2`
+
+Returns the `submission.submitted` body of that submit (without `webhook-*` headers). Without
+`sequence`, the latest submit. Use it when you missed a webhook.
+
+### 7.4 Set the status
+
+`POST submissions/{id}/status`
+
+```json
+{
+ "status": "changesRequested",
+ "message": "Thanks! Could you add a short intro and shorten the second section?"
+}
+```
+
+| Field | |
+|-------|--|
+| `status` | `inReview`, `changesRequested`, `declined` or `published` (§6.3) |
+| `message` | Optional Markdown, up to 10,000 characters. Shown to the contributor in the document |
+| `publicUrl` | Required with `published`: the `https://` URL of the post. The contributor gets a *View post* link |
+
+Answers with the submission as in §7.2.
+
+```bash
+curl -X POST https://wonky.work/api/pub/v1/submissions/sub_…/status \
+ -H "Authorization: Bearer $WONKY_API_KEY" -H "content-type: application/json" \
+ -d '{"status":"published","publicUrl":"https://blog.example.com/posts/faster-ci","message":"Live now 🎉"}'
+```
+
+### 7.5 Send a message
+
+`POST submissions/{id}/messages` with `{ "body": "Markdown, up to 10,000 characters" }` posts a
+message without changing the status, for example "Thanks, we'll look at it this week." Answers
+`{ "ok": true }`.
+
+### 7.6 Block a user
+
+`POST blocks` with `{ "wonkyUserId": "0191f0e2-…" }` stops that Wonky account from starting or
+submitting stories for your publication. `DELETE blocks/{wonkyUserId}` lifts the block. Both
+answer `{ "ok": true }`.
+
+### 7.7 Download an asset
+
+`GET assets/{assetId}` returns the image bytes with its content type (§5).
+
+## 8. Rotate secrets
+
+- **Signing secret**: *Rotate signing secret* shows the new secret once. For the next 24 hours
+ every webhook carries two signatures, one per secret, so you can deploy the new secret without
+ missing deliveries.
+- **API key**: *Rotate API key* shows a new key once, and the old key stops working at once.
+
+## 9. Test your setup
+
+1. Choose **Send test** on the publication page. Your receiver gets a `publication.ping` and the
+ page shows your server's answer.
+2. Open your contributor link with a second Wonky account, write a few lines, and press
+ **Submit**. Check your queue, then call the status API with `changesRequested` and a message,
+ and watch it appear in the document.
+3. **Deliveries** lists the last 50 webhooks with your server's status code and any error, and
+ can resend one.
+
+## 10. Checklist
+
+- [ ] Publication created; signing secret and API key stored server-side.
+- [ ] `/.well-known/wonky-publications.json` served and **Verify** passed.
+- [ ] *Write for us* link on the blog.
+- [ ] Webhook receiver: raw body kept, signature and timestamp checked, `webhook-id` deduplicated,
+ queue entry updated by `submission.id`, older `sequence` ignored, `2xx` within 10 seconds.
+- [ ] Images downloaded with the API key and hosted by you.
+- [ ] Moderation tool links to `documentUrl` and calls the status API for *Request changes*,
+ *Decline* and *Publish* (with the post URL).
+- [ ] Moderators' emails added to the publication.
+
+## 11. Troubleshooting
+
+| Problem | What to check |
+|---------|---------------|
+| *Send test* says the address is blocked | The webhook host must resolve to a public address; `localhost`, private networks and non-443 ports are refused. Use a public tunnel while you develop |
+| Every delivery fails with a 3xx status | Wonky doesn't follow redirects. Use the final URL (watch for `http`→`https` and trailing-slash redirects) |
+| Signatures never match | Sign the **raw** body: frameworks that parse JSON first and serialize it again change the bytes. Decode the secret from base64 after removing `whsec_` |
+| Valid signatures are rejected as too old | Your server's clock is off; sync it (NTP) |
+| `401 unauthorized` from the API | The key was rotated, or the header isn't exactly `Authorization: Bearer wpk_…` |
+| `409 invalidTransition` | The submission isn't in a status you can change (§6.3), for example because the contributor withdrew it |
+| Contributors see "not verified" | Serve `/.well-known/wonky-publications.json` on the site URL's host and choose **Verify** again |
diff --git a/packages/wonky_app/lib/l10n/wonky_app_en.arb b/packages/wonky_app/lib/l10n/wonky_app_en.arb
index 47b5921d..3c368b21 100644
--- a/packages/wonky_app/lib/l10n/wonky_app_en.arb
+++ b/packages/wonky_app/lib/l10n/wonky_app_en.arb
@@ -3060,5 +3060,152 @@
"type": "String"
}
}
- }
+ },
+ "settingsPublications": "Publications",
+ "@settingsPublications": {"description": "Wonky Work settings section: blogs that take stories from Wonky Write (specs/18)."},
+ "pubSectionHint": "Let people write for your blog in Wonky Write and send stories to your review queue with one button.",
+ "pubNew": "New publication",
+ "pubNewHint": "Wonky sends submitted stories to your webhook. You get the signing secret once, after you create the publication.",
+ "pubEdit": "Edit “{name}”",
+ "@pubEdit": {"placeholders": {"name": {"type": "String"}}},
+ "pubEditButton": "Edit…",
+ "pubGuide": "Developer guide",
+ "@pubGuide": {"description": "Opens the integration guide for blog owners."},
+ "pubEmpty": "You have no publications yet.",
+ "pubName": "Name",
+ "pubSiteUrl": "Site URL",
+ "pubWebhookUrl": "Webhook URL",
+ "pubTermsUrl": "Contributor terms URL (optional)",
+ "pubTemplate": "Template document (optional)",
+ "pubTemplateHint": "Paste a link to one of your Wonky Write documents",
+ "pubTemplateInvalid": "Paste the link of a Wonky Write document.",
+ "pubRequired": "Required.",
+ "pubHttpsOnly": "Use an https:// address.",
+ "pubFieldSummary": "Summary",
+ "pubFieldTags": "Tags",
+ "pubAllowedTags": "Allowed tags (optional)",
+ "pubAllowedTagsHint": "Comma separated; empty allows any tag",
+ "pubFieldCover": "Cover image",
+ "pubFieldBio": "Author bio",
+ "pubFieldNote": "Note to the moderators",
+ "pubFieldOff": "Not asked",
+ "pubFieldOptional": "Optional",
+ "pubFieldRequired": "Required",
+ "pubEnabled": "Accept new stories",
+ "pubHostVerified": "{host} ✓",
+ "@pubHostVerified": {"placeholders": {"host": {"type": "String"}}},
+ "pubHostUnverified": "{host} (not verified)",
+ "@pubHostUnverified": {"placeholders": {"host": {"type": "String"}}},
+ "pubDisabled": "Not accepting stories",
+ "pubOpenCount": "{count, plural, =0{No stories in review} =1{1 story in review} other{{count} stories in review}}",
+ "@pubOpenCount": {"placeholders": {"count": {"type": "int"}}},
+ "pubManage": "Manage",
+ "pubHide": "Hide",
+ "pubContributorLink": "Contributor link (put it behind “Write for us”)",
+ "pubId": "Publication id",
+ "pubCopy": "Copy",
+ "pubCopied": "Copied",
+ "pubDelete": "Delete",
+ "pubModerators": "Moderators",
+ "pubModeratorsHint": "Moderators get comment access to stories when they are submitted. Enter the email addresses of their Wonky accounts, one per line.",
+ "pubModeratorEmails": "Email addresses",
+ "pubModeratorsButton": "{count, plural, =0{Add moderators…} =1{1 moderator…} other{{count} moderators…}}",
+ "@pubModeratorsButton": {"placeholders": {"count": {"type": "int"}}},
+ "pubVerification": "Verification",
+ "pubVerifyHint": "Serve this at https://{host}/.well-known/wonky-publications.json, then verify. Until then contributors see your webhook's host and a warning.",
+ "@pubVerifyHint": {"placeholders": {"host": {"type": "String"}}},
+ "pubVerifiedAt": "{host} verified {date}",
+ "@pubVerifiedAt": {"placeholders": {"host": {"type": "String"}, "date": {"type": "String"}}},
+ "pubVerify": "Verify",
+ "pubVerified": "{host} is verified",
+ "@pubVerified": {"placeholders": {"host": {"type": "String"}}},
+ "pubVerifyFailed": "Not verified",
+ "pubCredentials": "Signing secret and API key",
+ "pubSecretHint": "Webhooks are signed with the signing secret. Your server calls the publication API with the API key.",
+ "pubRotationActive": "The previous signing secret still signs until {date}.",
+ "@pubRotationActive": {"placeholders": {"date": {"type": "String"}}},
+ "pubRotateSecret": "Rotate signing secret",
+ "pubRotateSecretBody": "You get a new secret. For 24 hours, webhooks carry signatures of the old and the new secret.",
+ "pubRotate": "Rotate",
+ "pubCreateApiKey": "Create API key",
+ "pubRotateApiKey": "Rotate API key",
+ "pubRotateApiKeyLast4": "Rotate API key (…{last4})",
+ "@pubRotateApiKeyLast4": {"placeholders": {"last4": {"type": "String"}}},
+ "pubRotateApiKeyBody": "The current key stops working at once.",
+ "pubApiKey": "API key",
+ "pubSigningSecret": "Signing secret",
+ "pubSecretOnce": "Copy the {what} now. It isn't shown again.",
+ "@pubSecretOnce": {"placeholders": {"what": {"type": "String"}}},
+ "pubSecretSaved": "I've stored it",
+ "pubDeliveries": "Deliveries",
+ "pubSendTest": "Send test",
+ "pubTestOk": "Your webhook accepted the test.",
+ "pubTestFailed": "Your webhook didn't accept the test",
+ "pubNoDeliveries": "No webhooks sent yet.",
+ "pubDelivered": "Delivered (HTTP {status_code})",
+ "@pubDelivered": {"placeholders": {"status_code": {"type": "int"}}},
+ "pubDeliveryFailed": "Failed: {error}",
+ "@pubDeliveryFailed": {"placeholders": {"error": {"type": "String"}}},
+ "pubDeliveryQueued": "Queued",
+ "pubDeliveryRetrying": "{error} · next try {date}",
+ "@pubDeliveryRetrying": {"placeholders": {"error": {"type": "String"}, "date": {"type": "String"}}},
+ "pubResend": "Resend",
+ "pubDeleteTitle": "Delete “{name}”?",
+ "@pubDeleteTitle": {"placeholders": {"name": {"type": "String"}}},
+ "pubDeleteBody": "Contributors' documents stay in their drives, but they can't submit them anymore, and moderators lose access. Your blog gets no webhook about it.",
+ "notifySubmissions": "Stories for publications",
+ "@notifySubmissions": {"description": "Notification settings: messages and status changes from blogs."},
+ "notifySubmissionsHint": "Status changes and messages from blogs you write for",
+ "notifSubmissionChanges": "{publication} asked for changes to “{what}”",
+ "@notifSubmissionChanges": {"placeholders": {"publication": {"type": "String"}, "what": {"type": "String"}}},
+ "notifSubmissionDeclined": "{publication} declined “{what}”",
+ "@notifSubmissionDeclined": {"placeholders": {"publication": {"type": "String"}, "what": {"type": "String"}}},
+ "notifSubmissionPublished": "{publication} published “{what}”",
+ "@notifSubmissionPublished": {"placeholders": {"publication": {"type": "String"}, "what": {"type": "String"}}},
+ "notifSubmissionInReview": "{publication} is reviewing “{what}”",
+ "@notifSubmissionInReview": {"placeholders": {"publication": {"type": "String"}, "what": {"type": "String"}}},
+ "notifSubmissionMessage": "{publication} sent a message about “{what}”",
+ "@notifSubmissionMessage": {"placeholders": {"publication": {"type": "String"}, "what": {"type": "String"}}},
+ "notifSubmissionFailed": "{publication} couldn't receive “{what}”",
+ "@notifSubmissionFailed": {"placeholders": {"publication": {"type": "String"}, "what": {"type": "String"}}},
+ "shareModerator": "Moderator · {publication}",
+ "@shareModerator": {"placeholders": {"publication": {"type": "String"}}},
+ "activitySubmitted": "{who} submitted it to {publication}",
+ "@activitySubmitted": {"placeholders": {"who": {"type": "String"}, "publication": {"type": "String"}}},
+ "activitySubmissionWithdrawn": "{who} withdrew it from {publication}",
+ "@activitySubmissionWithdrawn": {"placeholders": {"who": {"type": "String"}, "publication": {"type": "String"}}},
+ "activitySubmissionDisconnected": "{who} disconnected it from {publication}",
+ "@activitySubmissionDisconnected": {"placeholders": {"who": {"type": "String"}, "publication": {"type": "String"}}},
+ "activitySubmissionReconnected": "{who} connected it to {publication} again",
+ "@activitySubmissionReconnected": {"placeholders": {"who": {"type": "String"}, "publication": {"type": "String"}}},
+ "activitySubmissionStatus": "{publication}: {status}",
+ "@activitySubmissionStatus": {"placeholders": {"publication": {"type": "String"}, "status": {"type": "String"}}},
+ "submissionStatusDraft": "Draft",
+ "@submissionStatusDraft": {"description": "Status of a story for a publication."},
+ "submissionStatusInReview": "In review",
+ "submissionStatusChangesRequested": "Changes requested",
+ "submissionStatusDeclined": "Declined",
+ "submissionStatusPublished": "Published",
+ "submissionStatusWithdrawn": "Withdrawn",
+ "pubStartTitle": "Write for {name}",
+ "@pubStartTitle": {"description": "Consent dialog of a contributor link (specs/18 §3).", "placeholders": {"name": {"type": "String"}}},
+ "pubStartHostVerified": "{host} ✓",
+ "@pubStartHostVerified": {"placeholders": {"host": {"type": "String"}}},
+ "pubStartHostUnverified": "Wonky hasn't verified that this publication belongs to {site}. Your story is sent to {webhook}.",
+ "@pubStartHostUnverified": {"placeholders": {"site": {"type": "String"}, "webhook": {"type": "String"}}},
+ "pubStartConsent": "You'll write in your own Wonky document. When you press Submit, {name} receives the text and images of the document, the fields you fill in and your display name, and its moderators can read and comment on the document.",
+ "@pubStartConsent": {"placeholders": {"name": {"type": "String"}}},
+ "pubStartWhich": "Story",
+ "pubStartContinue": "Continue “{name}”",
+ "@pubStartContinue": {"placeholders": {"name": {"type": "String"}}},
+ "pubStartNew": "Start a new story",
+ "pubStartTerms": "I agree to {name}'s contributor terms ({url})",
+ "@pubStartTerms": {"placeholders": {"name": {"type": "String"}, "url": {"type": "String"}}},
+ "pubStartTermsRequired": "Agree to {name}'s contributor terms to start.",
+ "@pubStartTermsRequired": {"placeholders": {"name": {"type": "String"}}},
+ "pubStartButton": "Start writing",
+ "pubStartBlocked": "{name} isn't accepting stories from you.",
+ "@pubStartBlocked": {"placeholders": {"name": {"type": "String"}}},
+ "pubStartClosed": "{name} isn't accepting stories right now.",
+ "@pubStartClosed": {"placeholders": {"name": {"type": "String"}}}
}
diff --git a/packages/wonky_app/lib/l10n/wonky_app_en_XA.arb b/packages/wonky_app/lib/l10n/wonky_app_en_XA.arb
index 7a144d50..05033ee4 100644
--- a/packages/wonky_app/lib/l10n/wonky_app_en_XA.arb
+++ b/packages/wonky_app/lib/l10n/wonky_app_en_XA.arb
@@ -1316,5 +1316,112 @@
"boardReviewSpecs": "[Šþéçš on]",
"boardReviewRisks": "[Ŕîšķš on]",
"boardReviewUnrelated": "[Çĥåñĝéš ţĥé ţåšķ ðîðñ'ţ åšķ ƒöŕ one two th]",
- "boardReviewedSha": "[Ŕéṽîéŵéð åţ {sha} one]"
+ "boardReviewedSha": "[Ŕéṽîéŵéð åţ {sha} one]",
+ "settingsPublications": "[Þûƀļîçåţîöñš one t]",
+ "pubSectionHint": "[Ļéţ þéöþļé ŵŕîţé ƒöŕ ýöûŕ ƀļöĝ îñ Ŵöñķý Ŵŕîţé åñð šéñð šţöŕîéš ţö ýöûŕ ŕéṽîéŵ ǫûéûé ŵîţĥ öñé ƀûţţöñ. one two three four five six s]",
+ "pubNew": "[Ñéŵ þûƀļîçåţîöñ one t]",
+ "pubNewHint": "[Ŵöñķý šéñðš šûƀɱîţţéð šţöŕîéš ţö ýöûŕ ŵéƀĥööķ. Ýöû ĝéţ ţĥé šîĝñîñĝ šéçŕéţ öñçé, åƒţéŕ ýöû çŕéåţé ţĥé þûƀļîçåţîöñ. one two three four five six seven]",
+ "pubEdit": "[Éðîţ “{name}” one]",
+ "pubEditButton": "[Éðîţ… on]",
+ "pubGuide": "[Ðéṽéļöþéŕ ĝûîðé one t]",
+ "pubEmpty": "[Ýöû ĥåṽé ñö þûƀļîçåţîöñš ýéţ. one two t]",
+ "pubName": "[Ñåɱé on]",
+ "pubSiteUrl": "[Šîţé ÛŔĻ one]",
+ "pubWebhookUrl": "[Ŵéƀĥööķ ÛŔĻ one]",
+ "pubTermsUrl": "[Çöñţŕîƀûţöŕ ţéŕɱš ÛŔĻ (öþţîöñåļ) one two thr]",
+ "pubTemplate": "[Ţéɱþļåţé ðöçûɱéñţ (öþţîöñåļ) one two th]",
+ "pubTemplateHint": "[Þåšţé å ļîñķ ţö öñé öƒ ýöûŕ Ŵöñķý Ŵŕîţé ðöçûɱéñţš one two three]",
+ "pubTemplateInvalid": "[Þåšţé ţĥé ļîñķ öƒ å Ŵöñķý Ŵŕîţé ðöçûɱéñţ. one two thre]",
+ "pubRequired": "[Ŕéǫûîŕéð. one]",
+ "pubHttpsOnly": "[Ûšé åñ ĥţţþš:// åððŕéšš. one two]",
+ "pubFieldSummary": "[Šûɱɱåŕý one]",
+ "pubFieldTags": "[Ţåĝš on]",
+ "pubAllowedTags": "[Åļļöŵéð ţåĝš (öþţîöñåļ) one two]",
+ "pubAllowedTagsHint": "[Çöɱɱå šéþåŕåţéð; éɱþţý åļļöŵš åñý ţåĝ one two thre]",
+ "pubFieldCover": "[Çöṽéŕ îɱåĝé one]",
+ "pubFieldBio": "[Åûţĥöŕ ƀîö one]",
+ "pubFieldNote": "[Ñöţé ţö ţĥé ɱöðéŕåţöŕš one two]",
+ "pubFieldOff": "[Ñöţ åšķéð one]",
+ "pubFieldOptional": "[Öþţîöñåļ one]",
+ "pubFieldRequired": "[Ŕéǫûîŕéð one]",
+ "pubEnabled": "[Åççéþţ ñéŵ šţöŕîéš one tw]",
+ "pubHostVerified": "[{host} ✓ o]",
+ "pubHostUnverified": "[{host} (ñöţ ṽéŕîƒîéð) one t]",
+ "pubDisabled": "[Ñöţ åççéþţîñĝ šţöŕîéš one two]",
+ "pubOpenCount": "[{count, plural, =0{Ñö šţöŕîéš îñ ŕéṽîéŵ} =1{1 šţöŕý îñ ŕéṽîéŵ} other{{count} šţöŕîéš îñ ŕéṽîéŵ}} one two three fou]",
+ "pubManage": "[Ṁåñåĝé one]",
+ "pubHide": "[Ĥîðé on]",
+ "pubContributorLink": "[Çöñţŕîƀûţöŕ ļîñķ (þûţ îţ ƀéĥîñð “Ŵŕîţé ƒöŕ ûš”) one two three]",
+ "pubId": "[Þûƀļîçåţîöñ îð one t]",
+ "pubCopy": "[Çöþý on]",
+ "pubCopied": "[Çöþîéð one]",
+ "pubDelete": "[Ðéļéţé one]",
+ "pubModerators": "[Ṁöðéŕåţöŕš one]",
+ "pubModeratorsHint": "[Ṁöðéŕåţöŕš ĝéţ çöɱɱéñţ åççéšš ţö šţöŕîéš ŵĥéñ ţĥéý åŕé šûƀɱîţţéð. Éñţéŕ ţĥé éɱåîļ åððŕéššéš öƒ ţĥéîŕ Ŵöñķý åççöûñţš, öñé þéŕ ļîñé. one two three four five six seven eight]",
+ "pubModeratorEmails": "[Éɱåîļ åððŕéššéš one t]",
+ "pubModeratorsButton": "[{count, plural, =0{Åðð ɱöðéŕåţöŕš…} =1{1 ɱöðéŕåţöŕ…} other{{count} ɱöðéŕåţöŕš…}} one two three]",
+ "pubVerification": "[Ṽéŕîƒîçåţîöñ one t]",
+ "pubVerifyHint": "[Šéŕṽé ţĥîš åţ ĥţţþš://{host}/.ŵéļļ-ķñöŵñ/ŵöñķý-þûƀļîçåţîöñš.ĵšöñ, ţĥéñ ṽéŕîƒý. Ûñţîļ ţĥéñ çöñţŕîƀûţöŕš šéé ýöûŕ ŵéƀĥööķ'š ĥöšţ åñð å ŵåŕñîñĝ. one two three four five six seven eight ni]",
+ "pubVerifiedAt": "[{host} ṽéŕîƒîéð {date} one]",
+ "pubVerify": "[Ṽéŕîƒý one]",
+ "pubVerified": "[{host} îš ṽéŕîƒîéð one]",
+ "pubVerifyFailed": "[Ñöţ ṽéŕîƒîéð one]",
+ "pubCredentials": "[Šîĝñîñĝ šéçŕéţ åñð ÅÞÎ ķéý one two]",
+ "pubSecretHint": "[Ŵéƀĥööķš åŕé šîĝñéð ŵîţĥ ţĥé šîĝñîñĝ šéçŕéţ. Ýöûŕ šéŕṽéŕ çåļļš ţĥé þûƀļîçåţîöñ ÅÞÎ ŵîţĥ ţĥé ÅÞÎ ķéý. one two three four five six se]",
+ "pubRotationActive": "[Ţĥé þŕéṽîöûš šîĝñîñĝ šéçŕéţ šţîļļ šîĝñš ûñţîļ {date}. one two three]",
+ "pubRotateSecret": "[Ŕöţåţé šîĝñîñĝ šéçŕéţ one two]",
+ "pubRotateSecretBody": "[Ýöû ĝéţ å ñéŵ šéçŕéţ. Ƒöŕ 24 ĥöûŕš, ŵéƀĥööķš çåŕŕý šîĝñåţûŕéš öƒ ţĥé öļð åñð ţĥé ñéŵ šéçŕéţ. one two three four five six]",
+ "pubRotate": "[Ŕöţåţé one]",
+ "pubCreateApiKey": "[Çŕéåţé ÅÞÎ ķéý one t]",
+ "pubRotateApiKey": "[Ŕöţåţé ÅÞÎ ķéý one t]",
+ "pubRotateApiKeyLast4": "[Ŕöţåţé ÅÞÎ ķéý (…{last4}) one tw]",
+ "pubRotateApiKeyBody": "[Ţĥé çûŕŕéñţ ķéý šţöþš ŵöŕķîñĝ åţ öñçé. one two thre]",
+ "pubApiKey": "[ÅÞÎ ķéý one]",
+ "pubSigningSecret": "[Šîĝñîñĝ šéçŕéţ one t]",
+ "pubSecretOnce": "[Çöþý ţĥé {what} ñöŵ. Îţ îšñ'ţ šĥöŵñ åĝåîñ. one two thr]",
+ "pubSecretSaved": "[Î'ṽé šţöŕéð îţ one t]",
+ "pubDeliveries": "[Ðéļîṽéŕîéš one]",
+ "pubSendTest": "[Šéñð ţéšţ one]",
+ "pubTestOk": "[Ýöûŕ ŵéƀĥööķ åççéþţéð ţĥé ţéšţ. one two th]",
+ "pubTestFailed": "[Ýöûŕ ŵéƀĥööķ ðîðñ'ţ åççéþţ ţĥé ţéšţ one two thr]",
+ "pubNoDeliveries": "[Ñö ŵéƀĥööķš šéñţ ýéţ. one two]",
+ "pubDelivered": "[Ðéļîṽéŕéð (ĤŢŢÞ {status_code}) one tw]",
+ "pubDeliveryFailed": "[Ƒåîļéð: {error} one]",
+ "pubDeliveryQueued": "[Ǫûéûéð one]",
+ "pubDeliveryRetrying": "[{error} · ñéẋţ ţŕý {date} one]",
+ "pubResend": "[Ŕéšéñð one]",
+ "pubDeleteTitle": "[Ðéļéţé “{name}”? one]",
+ "pubDeleteBody": "[Çöñţŕîƀûţöŕš' ðöçûɱéñţš šţåý îñ ţĥéîŕ ðŕîṽéš, ƀûţ ţĥéý çåñ'ţ šûƀɱîţ ţĥéɱ åñýɱöŕé, åñð ɱöðéŕåţöŕš ļöšé åççéšš. Ýöûŕ ƀļöĝ ĝéţš ñö ŵéƀĥööķ åƀöûţ îţ. one two three four five six seven eight nine]",
+ "notifySubmissions": "[Šţöŕîéš ƒöŕ þûƀļîçåţîöñš one two]",
+ "notifySubmissionsHint": "[Šţåţûš çĥåñĝéš åñð ɱéššåĝéš ƒŕöɱ ƀļöĝš ýöû ŵŕîţé ƒöŕ one two three fo]",
+ "notifSubmissionChanges": "[{publication} åšķéð ƒöŕ çĥåñĝéš ţö “{what}” one two]",
+ "notifSubmissionDeclined": "[{publication} ðéçļîñéð “{what}” one]",
+ "notifSubmissionPublished": "[{publication} þûƀļîšĥéð “{what}” one]",
+ "notifSubmissionInReview": "[{publication} îš ŕéṽîéŵîñĝ “{what}” one t]",
+ "notifSubmissionMessage": "[{publication} šéñţ å ɱéššåĝé åƀöûţ “{what}” one two]",
+ "notifSubmissionFailed": "[{publication} çöûļðñ'ţ ŕéçéîṽé “{what}” one tw]",
+ "shareModerator": "[Ṁöðéŕåţöŕ · {publication} one]",
+ "activitySubmitted": "[{who} šûƀɱîţţéð îţ ţö {publication} one t]",
+ "activitySubmissionWithdrawn": "[{who} ŵîţĥðŕéŵ îţ ƒŕöɱ {publication} one t]",
+ "activitySubmissionDisconnected": "[{who} ðîšçöññéçţéð îţ ƒŕöɱ {publication} one two]",
+ "activitySubmissionReconnected": "[{who} çöññéçţéð îţ ţö {publication} åĝåîñ one two]",
+ "activitySubmissionStatus": "[{publication}: {status} o]",
+ "submissionStatusDraft": "[Ðŕåƒţ on]",
+ "submissionStatusInReview": "[Îñ ŕéṽîéŵ one]",
+ "submissionStatusChangesRequested": "[Çĥåñĝéš ŕéǫûéšţéð one tw]",
+ "submissionStatusDeclined": "[Ðéçļîñéð one]",
+ "submissionStatusPublished": "[Þûƀļîšĥéð one]",
+ "submissionStatusWithdrawn": "[Ŵîţĥðŕåŵñ one]",
+ "pubStartTitle": "[Ŵŕîţé ƒöŕ {name} one]",
+ "pubStartHostVerified": "[{host} ✓ o]",
+ "pubStartHostUnverified": "[Ŵöñķý ĥåšñ'ţ ṽéŕîƒîéð ţĥåţ ţĥîš þûƀļîçåţîöñ ƀéļöñĝš ţö {site}. Ýöûŕ šţöŕý îš šéñţ ţö {webhook}. one two three four five]",
+ "pubStartConsent": "[Ýöû'ļļ ŵŕîţé îñ ýöûŕ öŵñ Ŵöñķý ðöçûɱéñţ. Ŵĥéñ ýöû þŕéšš Šûƀɱîţ, {name} ŕéçéîṽéš ţĥé ţéẋţ åñð îɱåĝéš öƒ ţĥé ðöçûɱéñţ, ţĥé ƒîéļðš ýöû ƒîļļ îñ åñð ýöûŕ ðîšþļåý ñåɱé, åñð îţš ɱöðéŕåţöŕš çåñ ŕéåð åñð çöɱɱéñţ öñ ţĥé ðöçûɱéñţ. one two three four five six seven eight nine ten one two three]",
+ "pubStartWhich": "[Šţöŕý on]",
+ "pubStartContinue": "[Çöñţîñûé “{name}” one]",
+ "pubStartNew": "[Šţåŕţ å ñéŵ šţöŕý one t]",
+ "pubStartTerms": "[Î åĝŕéé ţö {name}'š çöñţŕîƀûţöŕ ţéŕɱš ({url}) one two th]",
+ "pubStartTermsRequired": "[Åĝŕéé ţö {name}'š çöñţŕîƀûţöŕ ţéŕɱš ţö šţåŕţ. one two thre]",
+ "pubStartButton": "[Šţåŕţ ŵŕîţîñĝ one t]",
+ "pubStartBlocked": "[{name} îšñ'ţ åççéþţîñĝ šţöŕîéš ƒŕöɱ ýöû. one two thr]",
+ "pubStartClosed": "[{name} îšñ'ţ åççéþţîñĝ šţöŕîéš ŕîĝĥţ ñöŵ. one two thr]"
}
diff --git a/packages/wonky_app/lib/src/activity/activity_feed.dart b/packages/wonky_app/lib/src/activity/activity_feed.dart
index 0cc0f27c..382fac12 100644
--- a/packages/wonky_app/lib/src/activity/activity_feed.dart
+++ b/packages/wonky_app/lib/src/activity/activity_feed.dart
@@ -216,6 +216,7 @@ IconData activityIcon(ActivityType t) => switch (t) {
ActivityType.restored => LucideIcons.archiveRestore,
ActivityType.exported => LucideIcons.download,
ActivityType.uploaded => LucideIcons.upload,
+ ActivityType.submission => LucideIcons.send,
};
class _EventAvatar extends StatelessWidget {
diff --git a/packages/wonky_app/lib/src/comments/comment_thread_actions.dart b/packages/wonky_app/lib/src/comments/comment_thread_actions.dart
index c653aa03..1921679c 100644
--- a/packages/wonky_app/lib/src/comments/comment_thread_actions.dart
+++ b/packages/wonky_app/lib/src/comments/comment_thread_actions.dart
@@ -93,7 +93,8 @@ class ThreadComment {
return ThreadComment(
id: '${cm.id}',
authorId: cm.author?.id,
- authorName: cm.author?.displayName,
+ // A publication's messages carry its name (specs/18 §5.2).
+ authorName: cm.authorLabel ?? cm.author?.displayName,
authorColor: cm.author?.avatarColor,
authorAvatarUrl: cm.author?.avatarUrl,
authorIsAgent: cm.authorKind.name != 'user',
diff --git a/packages/wonky_app/lib/src/config/home_scope.dart b/packages/wonky_app/lib/src/config/home_scope.dart
index 3dd5707e..9f5b959c 100644
--- a/packages/wonky_app/lib/src/config/home_scope.dart
+++ b/packages/wonky_app/lib/src/config/home_scope.dart
@@ -194,19 +194,32 @@ class HomeNavSpec {
/// The query of `/new?folder=&template=` (suite 03 §2, v2).
@immutable
class NewItemRequest {
- const NewItemRequest({this.folderUuid, this.templateUuid, this.name});
+ const NewItemRequest({
+ this.folderUuid,
+ this.templateUuid,
+ this.name,
+ this.publicationId,
+ this.ref,
+ });
/// Parses the route's query parameters (invalid uuids are ignored).
factory NewItemRequest.fromQuery(Map query) => NewItemRequest(
folderUuid: _uuid(query['folder']),
templateUuid: _uuid(query['template']),
name: query['name'],
+ publicationId: query['publication'],
+ ref: query['ref'],
);
final UuidValue? folderUuid;
final UuidValue? templateUuid;
final String? name;
+ /// (M14) A contributor link: start a story for this publication
+ /// (`pub_…`, specs/18 §3), with the blog's optional [ref].
+ final String? publicationId;
+ final String? ref;
+
/// The `/new` route of this request (relative to the app's base href):
/// `/new?folder=&template=`.
String get path {
@@ -214,6 +227,8 @@ class NewItemRequest {
if (folderUuid != null) 'folder': folderUuid!.uuid,
if (templateUuid != null) 'template': templateUuid!.uuid,
if (name != null && name!.isNotEmpty) 'name': name!,
+ 'publication': ?publicationId,
+ 'ref': ?ref,
};
return q.isEmpty
? '/new'
diff --git a/packages/wonky_app/lib/src/config/wonky_app_config.dart b/packages/wonky_app/lib/src/config/wonky_app_config.dart
index 135b08e5..2c0fb8a2 100644
--- a/packages/wonky_app/lib/src/config/wonky_app_config.dart
+++ b/packages/wonky_app/lib/src/config/wonky_app_config.dart
@@ -8,6 +8,7 @@ import '../bootstrap/app_urls.dart';
import '../commands/command.dart';
import '../mcp_bridge/ui_tool.dart';
import '../menus/shell_menus.dart';
+import '../publications/start_story.dart';
import '../shell/shell_services.dart';
import '../versions/version_renderer.dart';
import 'home_scope.dart';
@@ -228,12 +229,23 @@ abstract class WonkyAppConfig {
/// [createItem]; null (Drive) = the app has no `/new` route.
NewItemRouteHandler? get newItemRoute => primaryKind == null
? null
- : (context, services, request) => createItem(
- services,
- parentUuid: request.folderUuid,
- templateUuid: request.templateUuid,
- name: request.name,
- );
+ : (context, services, request) =>
+ // A contributor link starts a story (Write, specs/18 §3).
+ request.publicationId != null &&
+ primaryKind == DriveItemKind.document &&
+ services.repositories.hasPublications
+ ? startPublicationStory(
+ context,
+ services,
+ request.publicationId!,
+ ref: request.ref,
+ )
+ : createItem(
+ services,
+ parentUuid: request.folderUuid,
+ templateUuid: request.templateUuid,
+ name: request.name,
+ );
/// Handles `/import?file=` (*Open with*, Drive 02 §7.2);
/// null = the app has no import route (Drive, Slides). Write and Sheets
diff --git a/packages/wonky_app/lib/src/l10n/wonky_app_localizations.dart b/packages/wonky_app/lib/src/l10n/wonky_app_localizations.dart
index 54345271..350f7ad7 100644
--- a/packages/wonky_app/lib/src/l10n/wonky_app_localizations.dart
+++ b/packages/wonky_app/lib/src/l10n/wonky_app_localizations.dart
@@ -7995,6 +7995,648 @@ abstract class WonkyAppLocalizations {
/// In en, this message translates to:
/// **'Reviewed at {sha}'**
String boardReviewedSha(String sha);
+
+ /// Wonky Work settings section: blogs that take stories from Wonky Write (specs/18).
+ ///
+ /// In en, this message translates to:
+ /// **'Publications'**
+ String get settingsPublications;
+
+ /// No description provided for @pubSectionHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Let people write for your blog in Wonky Write and send stories to your review queue with one button.'**
+ String get pubSectionHint;
+
+ /// No description provided for @pubNew.
+ ///
+ /// In en, this message translates to:
+ /// **'New publication'**
+ String get pubNew;
+
+ /// No description provided for @pubNewHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Wonky sends submitted stories to your webhook. You get the signing secret once, after you create the publication.'**
+ String get pubNewHint;
+
+ /// No description provided for @pubEdit.
+ ///
+ /// In en, this message translates to:
+ /// **'Edit “{name}”'**
+ String pubEdit(String name);
+
+ /// No description provided for @pubEditButton.
+ ///
+ /// In en, this message translates to:
+ /// **'Edit…'**
+ String get pubEditButton;
+
+ /// Opens the integration guide for blog owners.
+ ///
+ /// In en, this message translates to:
+ /// **'Developer guide'**
+ String get pubGuide;
+
+ /// No description provided for @pubEmpty.
+ ///
+ /// In en, this message translates to:
+ /// **'You have no publications yet.'**
+ String get pubEmpty;
+
+ /// No description provided for @pubName.
+ ///
+ /// In en, this message translates to:
+ /// **'Name'**
+ String get pubName;
+
+ /// No description provided for @pubSiteUrl.
+ ///
+ /// In en, this message translates to:
+ /// **'Site URL'**
+ String get pubSiteUrl;
+
+ /// No description provided for @pubWebhookUrl.
+ ///
+ /// In en, this message translates to:
+ /// **'Webhook URL'**
+ String get pubWebhookUrl;
+
+ /// No description provided for @pubTermsUrl.
+ ///
+ /// In en, this message translates to:
+ /// **'Contributor terms URL (optional)'**
+ String get pubTermsUrl;
+
+ /// No description provided for @pubTemplate.
+ ///
+ /// In en, this message translates to:
+ /// **'Template document (optional)'**
+ String get pubTemplate;
+
+ /// No description provided for @pubTemplateHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Paste a link to one of your Wonky Write documents'**
+ String get pubTemplateHint;
+
+ /// No description provided for @pubTemplateInvalid.
+ ///
+ /// In en, this message translates to:
+ /// **'Paste the link of a Wonky Write document.'**
+ String get pubTemplateInvalid;
+
+ /// No description provided for @pubRequired.
+ ///
+ /// In en, this message translates to:
+ /// **'Required.'**
+ String get pubRequired;
+
+ /// No description provided for @pubHttpsOnly.
+ ///
+ /// In en, this message translates to:
+ /// **'Use an https:// address.'**
+ String get pubHttpsOnly;
+
+ /// No description provided for @pubFieldSummary.
+ ///
+ /// In en, this message translates to:
+ /// **'Summary'**
+ String get pubFieldSummary;
+
+ /// No description provided for @pubFieldTags.
+ ///
+ /// In en, this message translates to:
+ /// **'Tags'**
+ String get pubFieldTags;
+
+ /// No description provided for @pubAllowedTags.
+ ///
+ /// In en, this message translates to:
+ /// **'Allowed tags (optional)'**
+ String get pubAllowedTags;
+
+ /// No description provided for @pubAllowedTagsHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Comma separated; empty allows any tag'**
+ String get pubAllowedTagsHint;
+
+ /// No description provided for @pubFieldCover.
+ ///
+ /// In en, this message translates to:
+ /// **'Cover image'**
+ String get pubFieldCover;
+
+ /// No description provided for @pubFieldBio.
+ ///
+ /// In en, this message translates to:
+ /// **'Author bio'**
+ String get pubFieldBio;
+
+ /// No description provided for @pubFieldNote.
+ ///
+ /// In en, this message translates to:
+ /// **'Note to the moderators'**
+ String get pubFieldNote;
+
+ /// No description provided for @pubFieldOff.
+ ///
+ /// In en, this message translates to:
+ /// **'Not asked'**
+ String get pubFieldOff;
+
+ /// No description provided for @pubFieldOptional.
+ ///
+ /// In en, this message translates to:
+ /// **'Optional'**
+ String get pubFieldOptional;
+
+ /// No description provided for @pubFieldRequired.
+ ///
+ /// In en, this message translates to:
+ /// **'Required'**
+ String get pubFieldRequired;
+
+ /// No description provided for @pubEnabled.
+ ///
+ /// In en, this message translates to:
+ /// **'Accept new stories'**
+ String get pubEnabled;
+
+ /// No description provided for @pubHostVerified.
+ ///
+ /// In en, this message translates to:
+ /// **'{host} ✓'**
+ String pubHostVerified(String host);
+
+ /// No description provided for @pubHostUnverified.
+ ///
+ /// In en, this message translates to:
+ /// **'{host} (not verified)'**
+ String pubHostUnverified(String host);
+
+ /// No description provided for @pubDisabled.
+ ///
+ /// In en, this message translates to:
+ /// **'Not accepting stories'**
+ String get pubDisabled;
+
+ /// No description provided for @pubOpenCount.
+ ///
+ /// In en, this message translates to:
+ /// **'{count, plural, =0{No stories in review} =1{1 story in review} other{{count} stories in review}}'**
+ String pubOpenCount(int count);
+
+ /// No description provided for @pubManage.
+ ///
+ /// In en, this message translates to:
+ /// **'Manage'**
+ String get pubManage;
+
+ /// No description provided for @pubHide.
+ ///
+ /// In en, this message translates to:
+ /// **'Hide'**
+ String get pubHide;
+
+ /// No description provided for @pubContributorLink.
+ ///
+ /// In en, this message translates to:
+ /// **'Contributor link (put it behind “Write for us”)'**
+ String get pubContributorLink;
+
+ /// No description provided for @pubId.
+ ///
+ /// In en, this message translates to:
+ /// **'Publication id'**
+ String get pubId;
+
+ /// No description provided for @pubCopy.
+ ///
+ /// In en, this message translates to:
+ /// **'Copy'**
+ String get pubCopy;
+
+ /// No description provided for @pubCopied.
+ ///
+ /// In en, this message translates to:
+ /// **'Copied'**
+ String get pubCopied;
+
+ /// No description provided for @pubDelete.
+ ///
+ /// In en, this message translates to:
+ /// **'Delete'**
+ String get pubDelete;
+
+ /// No description provided for @pubModerators.
+ ///
+ /// In en, this message translates to:
+ /// **'Moderators'**
+ String get pubModerators;
+
+ /// No description provided for @pubModeratorsHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Moderators get comment access to stories when they are submitted. Enter the email addresses of their Wonky accounts, one per line.'**
+ String get pubModeratorsHint;
+
+ /// No description provided for @pubModeratorEmails.
+ ///
+ /// In en, this message translates to:
+ /// **'Email addresses'**
+ String get pubModeratorEmails;
+
+ /// No description provided for @pubModeratorsButton.
+ ///
+ /// In en, this message translates to:
+ /// **'{count, plural, =0{Add moderators…} =1{1 moderator…} other{{count} moderators…}}'**
+ String pubModeratorsButton(int count);
+
+ /// No description provided for @pubVerification.
+ ///
+ /// In en, this message translates to:
+ /// **'Verification'**
+ String get pubVerification;
+
+ /// No description provided for @pubVerifyHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Serve this at https://{host}/.well-known/wonky-publications.json, then verify. Until then contributors see your webhook\'s host and a warning.'**
+ String pubVerifyHint(String host);
+
+ /// No description provided for @pubVerifiedAt.
+ ///
+ /// In en, this message translates to:
+ /// **'{host} verified {date}'**
+ String pubVerifiedAt(String host, String date);
+
+ /// No description provided for @pubVerify.
+ ///
+ /// In en, this message translates to:
+ /// **'Verify'**
+ String get pubVerify;
+
+ /// No description provided for @pubVerified.
+ ///
+ /// In en, this message translates to:
+ /// **'{host} is verified'**
+ String pubVerified(String host);
+
+ /// No description provided for @pubVerifyFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Not verified'**
+ String get pubVerifyFailed;
+
+ /// No description provided for @pubCredentials.
+ ///
+ /// In en, this message translates to:
+ /// **'Signing secret and API key'**
+ String get pubCredentials;
+
+ /// No description provided for @pubSecretHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Webhooks are signed with the signing secret. Your server calls the publication API with the API key.'**
+ String get pubSecretHint;
+
+ /// No description provided for @pubRotationActive.
+ ///
+ /// In en, this message translates to:
+ /// **'The previous signing secret still signs until {date}.'**
+ String pubRotationActive(String date);
+
+ /// No description provided for @pubRotateSecret.
+ ///
+ /// In en, this message translates to:
+ /// **'Rotate signing secret'**
+ String get pubRotateSecret;
+
+ /// No description provided for @pubRotateSecretBody.
+ ///
+ /// In en, this message translates to:
+ /// **'You get a new secret. For 24 hours, webhooks carry signatures of the old and the new secret.'**
+ String get pubRotateSecretBody;
+
+ /// No description provided for @pubRotate.
+ ///
+ /// In en, this message translates to:
+ /// **'Rotate'**
+ String get pubRotate;
+
+ /// No description provided for @pubCreateApiKey.
+ ///
+ /// In en, this message translates to:
+ /// **'Create API key'**
+ String get pubCreateApiKey;
+
+ /// No description provided for @pubRotateApiKey.
+ ///
+ /// In en, this message translates to:
+ /// **'Rotate API key'**
+ String get pubRotateApiKey;
+
+ /// No description provided for @pubRotateApiKeyLast4.
+ ///
+ /// In en, this message translates to:
+ /// **'Rotate API key (…{last4})'**
+ String pubRotateApiKeyLast4(String last4);
+
+ /// No description provided for @pubRotateApiKeyBody.
+ ///
+ /// In en, this message translates to:
+ /// **'The current key stops working at once.'**
+ String get pubRotateApiKeyBody;
+
+ /// No description provided for @pubApiKey.
+ ///
+ /// In en, this message translates to:
+ /// **'API key'**
+ String get pubApiKey;
+
+ /// No description provided for @pubSigningSecret.
+ ///
+ /// In en, this message translates to:
+ /// **'Signing secret'**
+ String get pubSigningSecret;
+
+ /// No description provided for @pubSecretOnce.
+ ///
+ /// In en, this message translates to:
+ /// **'Copy the {what} now. It isn\'t shown again.'**
+ String pubSecretOnce(String what);
+
+ /// No description provided for @pubSecretSaved.
+ ///
+ /// In en, this message translates to:
+ /// **'I\'ve stored it'**
+ String get pubSecretSaved;
+
+ /// No description provided for @pubDeliveries.
+ ///
+ /// In en, this message translates to:
+ /// **'Deliveries'**
+ String get pubDeliveries;
+
+ /// No description provided for @pubSendTest.
+ ///
+ /// In en, this message translates to:
+ /// **'Send test'**
+ String get pubSendTest;
+
+ /// No description provided for @pubTestOk.
+ ///
+ /// In en, this message translates to:
+ /// **'Your webhook accepted the test.'**
+ String get pubTestOk;
+
+ /// No description provided for @pubTestFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Your webhook didn\'t accept the test'**
+ String get pubTestFailed;
+
+ /// No description provided for @pubNoDeliveries.
+ ///
+ /// In en, this message translates to:
+ /// **'No webhooks sent yet.'**
+ String get pubNoDeliveries;
+
+ /// No description provided for @pubDelivered.
+ ///
+ /// In en, this message translates to:
+ /// **'Delivered (HTTP {status_code})'**
+ String pubDelivered(int status_code);
+
+ /// No description provided for @pubDeliveryFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Failed: {error}'**
+ String pubDeliveryFailed(String error);
+
+ /// No description provided for @pubDeliveryQueued.
+ ///
+ /// In en, this message translates to:
+ /// **'Queued'**
+ String get pubDeliveryQueued;
+
+ /// No description provided for @pubDeliveryRetrying.
+ ///
+ /// In en, this message translates to:
+ /// **'{error} · next try {date}'**
+ String pubDeliveryRetrying(String error, String date);
+
+ /// No description provided for @pubResend.
+ ///
+ /// In en, this message translates to:
+ /// **'Resend'**
+ String get pubResend;
+
+ /// No description provided for @pubDeleteTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Delete “{name}”?'**
+ String pubDeleteTitle(String name);
+
+ /// No description provided for @pubDeleteBody.
+ ///
+ /// In en, this message translates to:
+ /// **'Contributors\' documents stay in their drives, but they can\'t submit them anymore, and moderators lose access. Your blog gets no webhook about it.'**
+ String get pubDeleteBody;
+
+ /// Notification settings: messages and status changes from blogs.
+ ///
+ /// In en, this message translates to:
+ /// **'Stories for publications'**
+ String get notifySubmissions;
+
+ /// No description provided for @notifySubmissionsHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Status changes and messages from blogs you write for'**
+ String get notifySubmissionsHint;
+
+ /// No description provided for @notifSubmissionChanges.
+ ///
+ /// In en, this message translates to:
+ /// **'{publication} asked for changes to “{what}”'**
+ String notifSubmissionChanges(String publication, String what);
+
+ /// No description provided for @notifSubmissionDeclined.
+ ///
+ /// In en, this message translates to:
+ /// **'{publication} declined “{what}”'**
+ String notifSubmissionDeclined(String publication, String what);
+
+ /// No description provided for @notifSubmissionPublished.
+ ///
+ /// In en, this message translates to:
+ /// **'{publication} published “{what}”'**
+ String notifSubmissionPublished(String publication, String what);
+
+ /// No description provided for @notifSubmissionInReview.
+ ///
+ /// In en, this message translates to:
+ /// **'{publication} is reviewing “{what}”'**
+ String notifSubmissionInReview(String publication, String what);
+
+ /// No description provided for @notifSubmissionMessage.
+ ///
+ /// In en, this message translates to:
+ /// **'{publication} sent a message about “{what}”'**
+ String notifSubmissionMessage(String publication, String what);
+
+ /// No description provided for @notifSubmissionFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'{publication} couldn\'t receive “{what}”'**
+ String notifSubmissionFailed(String publication, String what);
+
+ /// No description provided for @shareModerator.
+ ///
+ /// In en, this message translates to:
+ /// **'Moderator · {publication}'**
+ String shareModerator(String publication);
+
+ /// No description provided for @activitySubmitted.
+ ///
+ /// In en, this message translates to:
+ /// **'{who} submitted it to {publication}'**
+ String activitySubmitted(String who, String publication);
+
+ /// No description provided for @activitySubmissionWithdrawn.
+ ///
+ /// In en, this message translates to:
+ /// **'{who} withdrew it from {publication}'**
+ String activitySubmissionWithdrawn(String who, String publication);
+
+ /// No description provided for @activitySubmissionDisconnected.
+ ///
+ /// In en, this message translates to:
+ /// **'{who} disconnected it from {publication}'**
+ String activitySubmissionDisconnected(String who, String publication);
+
+ /// No description provided for @activitySubmissionReconnected.
+ ///
+ /// In en, this message translates to:
+ /// **'{who} connected it to {publication} again'**
+ String activitySubmissionReconnected(String who, String publication);
+
+ /// No description provided for @activitySubmissionStatus.
+ ///
+ /// In en, this message translates to:
+ /// **'{publication}: {status}'**
+ String activitySubmissionStatus(String publication, String status);
+
+ /// Status of a story for a publication.
+ ///
+ /// In en, this message translates to:
+ /// **'Draft'**
+ String get submissionStatusDraft;
+
+ /// No description provided for @submissionStatusInReview.
+ ///
+ /// In en, this message translates to:
+ /// **'In review'**
+ String get submissionStatusInReview;
+
+ /// No description provided for @submissionStatusChangesRequested.
+ ///
+ /// In en, this message translates to:
+ /// **'Changes requested'**
+ String get submissionStatusChangesRequested;
+
+ /// No description provided for @submissionStatusDeclined.
+ ///
+ /// In en, this message translates to:
+ /// **'Declined'**
+ String get submissionStatusDeclined;
+
+ /// No description provided for @submissionStatusPublished.
+ ///
+ /// In en, this message translates to:
+ /// **'Published'**
+ String get submissionStatusPublished;
+
+ /// No description provided for @submissionStatusWithdrawn.
+ ///
+ /// In en, this message translates to:
+ /// **'Withdrawn'**
+ String get submissionStatusWithdrawn;
+
+ /// Consent dialog of a contributor link (specs/18 §3).
+ ///
+ /// In en, this message translates to:
+ /// **'Write for {name}'**
+ String pubStartTitle(String name);
+
+ /// No description provided for @pubStartHostVerified.
+ ///
+ /// In en, this message translates to:
+ /// **'{host} ✓'**
+ String pubStartHostVerified(String host);
+
+ /// No description provided for @pubStartHostUnverified.
+ ///
+ /// In en, this message translates to:
+ /// **'Wonky hasn\'t verified that this publication belongs to {site}. Your story is sent to {webhook}.'**
+ String pubStartHostUnverified(String site, String webhook);
+
+ /// No description provided for @pubStartConsent.
+ ///
+ /// In en, this message translates to:
+ /// **'You\'ll write in your own Wonky document. When you press Submit, {name} receives the text and images of the document, the fields you fill in and your display name, and its moderators can read and comment on the document.'**
+ String pubStartConsent(String name);
+
+ /// No description provided for @pubStartWhich.
+ ///
+ /// In en, this message translates to:
+ /// **'Story'**
+ String get pubStartWhich;
+
+ /// No description provided for @pubStartContinue.
+ ///
+ /// In en, this message translates to:
+ /// **'Continue “{name}”'**
+ String pubStartContinue(String name);
+
+ /// No description provided for @pubStartNew.
+ ///
+ /// In en, this message translates to:
+ /// **'Start a new story'**
+ String get pubStartNew;
+
+ /// No description provided for @pubStartTerms.
+ ///
+ /// In en, this message translates to:
+ /// **'I agree to {name}\'s contributor terms ({url})'**
+ String pubStartTerms(String name, String url);
+
+ /// No description provided for @pubStartTermsRequired.
+ ///
+ /// In en, this message translates to:
+ /// **'Agree to {name}\'s contributor terms to start.'**
+ String pubStartTermsRequired(String name);
+
+ /// No description provided for @pubStartButton.
+ ///
+ /// In en, this message translates to:
+ /// **'Start writing'**
+ String get pubStartButton;
+
+ /// No description provided for @pubStartBlocked.
+ ///
+ /// In en, this message translates to:
+ /// **'{name} isn\'t accepting stories from you.'**
+ String pubStartBlocked(String name);
+
+ /// No description provided for @pubStartClosed.
+ ///
+ /// In en, this message translates to:
+ /// **'{name} isn\'t accepting stories right now.'**
+ String pubStartClosed(String name);
}
class _WonkyAppLocalizationsDelegate
diff --git a/packages/wonky_app/lib/src/l10n/wonky_app_localizations_en.dart b/packages/wonky_app/lib/src/l10n/wonky_app_localizations_en.dart
index 3dca68a9..90b75c80 100644
--- a/packages/wonky_app/lib/src/l10n/wonky_app_localizations_en.dart
+++ b/packages/wonky_app/lib/src/l10n/wonky_app_localizations_en.dart
@@ -4608,4685 +4608,5521 @@ class WonkyAppLocalizationsEn extends WonkyAppLocalizations {
String boardReviewedSha(String sha) {
return 'Reviewed at $sha';
}
-}
-
-/// The translations for English (`en_XA`).
-class WonkyAppLocalizationsEnXa extends WonkyAppLocalizationsEn {
- WonkyAppLocalizationsEnXa() : super('en_XA');
@override
- String get aboutAppVersion => '[Åþþ ṽéŕšîöñ one]';
+ String get settingsPublications => 'Publications';
@override
- String get aboutLicenses => '[Öþéñ šöûŕçé ļîçéñšéš one two]';
+ String get pubSectionHint =>
+ 'Let people write for your blog in Wonky Write and send stories to your review queue with one button.';
@override
- String get aboutProtocol => '[Þŕöţöçöļ one]';
+ String get pubNew => 'New publication';
@override
- String get aboutRunMode => '[Šéŕṽéŕ ɱöðé one]';
+ String get pubNewHint =>
+ 'Wonky sends submitted stories to your webhook. You get the signing secret once, after you create the publication.';
@override
- String get aboutServerVersion => '[Šéŕṽéŕ ṽéŕšîöñ one t]';
+ String pubEdit(String name) {
+ return 'Edit “$name”';
+ }
@override
- String get accountMenu => '[Åççöûñţ one]';
+ String get pubEditButton => 'Edit…';
@override
- String get actionBack => '[Ɓåçķ on]';
+ String get pubGuide => 'Developer guide';
@override
- String get actionCancel => '[Çåñçéļ one]';
+ String get pubEmpty => 'You have no publications yet.';
@override
- String get actionClear => '[Çļéåŕ on]';
+ String get pubName => 'Name';
@override
- String get actionClose => '[Çļöšé on]';
+ String get pubSiteUrl => 'Site URL';
@override
- String get actionCopy => '[Çöþý on]';
+ String get pubWebhookUrl => 'Webhook URL';
@override
- String get actionCreate => '[Çŕéåţé one]';
+ String get pubTermsUrl => 'Contributor terms URL (optional)';
@override
- String get actionDone => '[Ðöñé on]';
+ String get pubTemplate => 'Template document (optional)';
@override
- String get actionOk => '[ÖĶ o]';
+ String get pubTemplateHint =>
+ 'Paste a link to one of your Wonky Write documents';
@override
- String get actionOpen => '[Öþéñ on]';
+ String get pubTemplateInvalid => 'Paste the link of a Wonky Write document.';
@override
- String get actionReload => '[Ŕéļöåð one]';
+ String get pubRequired => 'Required.';
@override
- String get actionRetry => '[Ŕéţŕý on]';
+ String get pubHttpsOnly => 'Use an https:// address.';
@override
- String get actionSave => '[Šåṽé on]';
+ String get pubFieldSummary => 'Summary';
@override
- String get actionSend => '[Šéñð on]';
+ String get pubFieldTags => 'Tags';
@override
- String get actionShare => '[Šĥåŕé on]';
+ String get pubAllowedTags => 'Allowed tags (optional)';
@override
- String get actionStop => '[Šţöþ on]';
+ String get pubAllowedTagsHint => 'Comma separated; empty allows any tag';
@override
- String get actionUndo => '[Ûñðö on]';
+ String get pubFieldCover => 'Cover image';
@override
- String activityCommented(String who) {
- return '[$who çöɱɱéñţéð one]';
- }
+ String get pubFieldBio => 'Author bio';
@override
- String activityCreated(String who) {
- return '[$who çŕéåţéð ţĥîš one]';
- }
+ String get pubFieldNote => 'Note to the moderators';
@override
- String activityEdited(String who) {
- return '[$who éðîţéð one]';
- }
+ String get pubFieldOff => 'Not asked';
@override
- String get activityEmpty => '[Ñö åçţîṽîţý ýéţ. one t]';
+ String get pubFieldOptional => 'Optional';
@override
- String activityExported(String who) {
- return '[$who éẋþöŕţéð one]';
- }
+ String get pubFieldRequired => 'Required';
@override
- String activityMoved(String who) {
- return '[$who ɱöṽéð ţĥîš one]';
- }
+ String get pubEnabled => 'Accept new stories';
@override
- String activityRenamed(String who) {
- return '[$who ŕéñåɱéð ţĥîš one]';
+ String pubHostVerified(String host) {
+ return '$host ✓';
}
@override
- String activityRestored(String who) {
- return '[$who ŕéšţöŕéð ţĥîš ƒŕöɱ ţĥé ţŕåšĥ one two t]';
+ String pubHostUnverified(String host) {
+ return '$host (not verified)';
}
@override
- String activityRestoredVersion(String who) {
- return '[$who ŕéšţöŕéð å ṽéŕšîöñ one tw]';
- }
+ String get pubDisabled => 'Not accepting stories';
@override
- String activityShared(String who) {
- return '[$who šĥåŕéð ţĥîš one]';
+ String pubOpenCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count stories in review',
+ one: '1 story in review',
+ zero: 'No stories in review',
+ );
+ return '$_temp0';
}
@override
- String get activitySystem => '[Ŵöñķý Ŵöŕķ one]';
+ String get pubManage => 'Manage';
@override
- String activityTrashed(String who) {
- return '[$who ɱöṽéð ţĥîš ţö ţĥé ţŕåšĥ one two]';
- }
+ String get pubHide => 'Hide';
@override
- String activityUnshared(String who) {
- return '[$who ŕéɱöṽéð åççéšš one t]';
- }
+ String get pubContributorLink =>
+ 'Contributor link (put it behind “Write for us”)';
@override
- String activityUploaded(String who) {
- return '[$who ûþļöåðéð ţĥîš one t]';
- }
+ String get pubId => 'Publication id';
@override
- String get addShortcutAction => '[Åðð šĥöŕţçûţ one]';
+ String get pubCopy => 'Copy';
@override
- String get addShortcutTitle => '[Åðð šĥöŕţçûţ ţö Ṁý Ðŕîṽé one two]';
+ String get pubCopied => 'Copied';
@override
- String agentUndoLabel(String label) {
- return '[ÅÎ: $label on]';
- }
+ String get pubDelete => 'Delete';
@override
- String get aiEnable => '[Éñåƀļé ţĥé åššîšţåñţ one two]';
+ String get pubModerators => 'Moderators';
@override
- String get authBackToSignIn => '[Ɓåçķ ţö šîĝñ îñ one t]';
+ String get pubModeratorsHint =>
+ 'Moderators get comment access to stories when they are submitted. Enter the email addresses of their Wonky accounts, one per line.';
@override
- String get authBackToSignUp => '[Ɓåçķ ţö šîĝñ ûþ one t]';
+ String get pubModeratorEmails => 'Email addresses';
@override
- String get authChangeEmail => '[Çĥåñĝé éɱåîļ åððŕéšš one two]';
+ String pubModeratorsButton(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count moderators…',
+ one: '1 moderator…',
+ zero: 'Add moderators…',
+ );
+ return '$_temp0';
+ }
@override
- String get authCodeResent => '[Å ñéŵ çöðé îš öñ îţš ŵåý. one two]';
+ String get pubVerification => 'Verification';
@override
- String get authConfirmPassword => '[Çöñƒîŕɱ þåššŵöŕð one tw]';
+ String pubVerifyHint(String host) {
+ return 'Serve this at https://$host/.well-known/wonky-publications.json, then verify. Until then contributors see your webhook\'s host and a warning.';
+ }
@override
- String get authCreateAccount => '[Çŕéåţé åççöûñţ one t]';
+ String pubVerifiedAt(String host, String date) {
+ return '$host verified $date';
+ }
@override
- String get authDisplayName => '[Ñåɱé on]';
+ String get pubVerify => 'Verify';
@override
- String get authEmail => '[Éɱåîļ on]';
+ String pubVerified(String host) {
+ return '$host is verified';
+ }
@override
- String get authErrorCodeLength => '[Éñţéŕ ţĥé 6-ðîĝîţ çöðé. one two]';
+ String get pubVerifyFailed => 'Not verified';
@override
- String get authErrorEmail => '[Éñţéŕ å ṽåļîð éɱåîļ åððŕéšš. one two t]';
+ String get pubCredentials => 'Signing secret and API key';
@override
- String get authErrorExpiredCode =>
- '[Ţĥîš çöðé ĥåš éẋþîŕéð. Ŕéǫûéšţ å ñéŵ öñé. one two thre]';
+ String get pubSecretHint =>
+ 'Webhooks are signed with the signing secret. Your server calls the publication API with the API key.';
@override
- String get authErrorInvalidCode =>
- '[Ţĥåţ çöðé îš ñöţ ŕîĝĥţ. Çĥéçķ îţ åñð ţŕý åĝåîñ. one two three]';
+ String pubRotationActive(String date) {
+ return 'The previous signing secret still signs until $date.';
+ }
@override
- String get authErrorInvalidCredentials =>
- '[Ŵŕöñĝ éɱåîļ öŕ þåššŵöŕð. one two]';
+ String get pubRotateSecret => 'Rotate signing secret';
@override
- String get authErrorNetwork =>
- '[Çåñ\'ţ ŕéåçĥ ţĥé šéŕṽéŕ. Çĥéçķ ýöûŕ çöññéçţîöñ åñð ţŕý åĝåîñ. one two three four]';
+ String get pubRotateSecretBody =>
+ 'You get a new secret. For 24 hours, webhooks carry signatures of the old and the new secret.';
@override
- String get authErrorPasswordMismatch =>
- '[Ţĥé þåššŵöŕðš ðöñ\'ţ ɱåţçĥ. one two t]';
+ String get pubRotate => 'Rotate';
@override
- String get authErrorPasswordPolicy =>
- '[Ţĥîš þåššŵöŕð ðöéšñ\'ţ ɱééţ ţĥé ŕéǫûîŕéɱéñţš. Çĥööšé å ļöñĝéŕ öŕ ɱöŕé ṽåŕîéð þåššŵöŕð. one two three four five si]';
+ String get pubCreateApiKey => 'Create API key';
@override
- String get authErrorPasswordShort => '[Ûšé åţ ļéåšţ 8 çĥåŕåçţéŕš. one two]';
+ String get pubRotateApiKey => 'Rotate API key';
@override
- String get authErrorPasswordWeak =>
- '[Ţĥîš þåššŵöŕð îš ţöö éåšý ţö ĝûéšš. one two thr]';
+ String pubRotateApiKeyLast4(String last4) {
+ return 'Rotate API key (…$last4)';
+ }
@override
- String get authErrorTerms =>
- '[Þļéåšé åççéþţ ţĥé ţéŕɱš ţö çöñţîñûé. one two thr]';
+ String get pubRotateApiKeyBody => 'The current key stops working at once.';
@override
- String get authErrorTooManyAttempts =>
- '[Ţöö ɱåñý åţţéɱþţš. Ŵåîţ å ɱöɱéñţ åñð ţŕý åĝåîñ. one two three]';
+ String get pubApiKey => 'API key';
@override
- String get authContinueWithGoogle => '[Çöñţîñûé ŵîţĥ Ĝööĝļé one two]';
+ String get pubSigningSecret => 'Signing secret';
@override
- String get authOr => '[öŕ o]';
+ String pubSecretOnce(String what) {
+ return 'Copy the $what now. It isn\'t shown again.';
+ }
@override
- String get authGoogleTerms =>
- '[Ɓý çöñţîñûîñĝ ŵîţĥ Ĝööĝļé, ýöû åĝŕéé ţö ţĥé ţéŕɱš öƒ šéŕṽîçé åñð ţĥé þŕîṽåçý þöļîçý. one two three four five s]';
+ String get pubSecretSaved => 'I\'ve stored it';
@override
- String get authErrorGoogleUnavailable =>
- '[Šîĝñ îñ ŵîţĥ Ĝööĝļé îšñ\'ţ šéţ ûþ öñ ţĥîš šéŕṽéŕ. one two three]';
+ String get pubDeliveries => 'Deliveries';
@override
- String get authErrorPopupBlocked =>
- '[Ýöûŕ ƀŕöŵšéŕ ƀļöçķéð ţĥé Ĝööĝļé šîĝñ-îñ ŵîñðöŵ. Åļļöŵ þöþ-ûþš ƒöŕ ţĥîš šîţé åñð ţŕý åĝåîñ. one two three four five six]';
+ String get pubSendTest => 'Send test';
@override
- String get authErrorUnknown =>
- '[Šöɱéţĥîñĝ ŵéñţ ŵŕöñĝ. Þļéåšé ţŕý åĝåîñ. one two thre]';
+ String get pubTestOk => 'Your webhook accepted the test.';
@override
- String get authForgotPassword => '[Ƒöŕĝöţ þåššŵöŕð? one tw]';
+ String get pubTestFailed => 'Your webhook didn\'t accept the test';
@override
- String get authHaveAccount => '[Åļŕéåðý ĥåṽé åñ åççöûñţ? one two]';
+ String get pubNoDeliveries => 'No webhooks sent yet.';
@override
- String get authHidePassword => '[Ĥîðé þåššŵöŕð one t]';
+ String pubDelivered(int status_code) {
+ return 'Delivered (HTTP $status_code)';
+ }
@override
- String get authNewPassword => '[Ñéŵ þåššŵöŕð one]';
+ String pubDeliveryFailed(String error) {
+ return 'Failed: $error';
+ }
@override
- String get authNewPasswordSubtitle =>
- '[Çĥööšé å ñéŵ þåššŵöŕð ƒöŕ ýöûŕ åççöûñţ. one two thre]';
+ String get pubDeliveryQueued => 'Queued';
@override
- String get authNoAccount => '[Ñéŵ ţö Ŵöñķý Ŵöŕķ? one tw]';
+ String pubDeliveryRetrying(String error, String date) {
+ return '$error · next try $date';
+ }
@override
- String get authPassword => '[Þåššŵöŕð one]';
+ String get pubResend => 'Resend';
@override
- String get authRememberMe => '[Ķééþ ɱé šîĝñéð îñ one t]';
+ String pubDeleteTitle(String name) {
+ return 'Delete “$name”?';
+ }
@override
- String get authResendCode => '[Ŕéšéñð çöðé one]';
+ String get pubDeleteBody =>
+ 'Contributors\' documents stay in their drives, but they can\'t submit them anymore, and moderators lose access. Your blog gets no webhook about it.';
@override
- String authResendIn(int seconds) {
- return '[Ŕéšéñð çöðé îñ $seconds š one t]';
- }
+ String get notifySubmissions => 'Stories for publications';
@override
- String get authResetSubtitle =>
- '[Ŵé\'ļļ éɱåîļ ýöû å çöðé ţö ŕéšéţ ýöûŕ þåššŵöŕð. one two three]';
+ String get notifySubmissionsHint =>
+ 'Status changes and messages from blogs you write for';
@override
- String get authResetTitle => '[Ŕéšéţ ýöûŕ þåššŵöŕð one tw]';
+ String notifSubmissionChanges(String publication, String what) {
+ return '$publication asked for changes to “$what”';
+ }
@override
- String get authSendCode => '[Šéñð çöðé one]';
+ String notifSubmissionDeclined(String publication, String what) {
+ return '$publication declined “$what”';
+ }
@override
- String get authSetPassword => '[Šéţ þåššŵöŕð åñð šîĝñ îñ one two]';
+ String notifSubmissionPublished(String publication, String what) {
+ return '$publication published “$what”';
+ }
@override
- String get authShowPassword => '[Šĥöŵ þåššŵöŕð one t]';
+ String notifSubmissionInReview(String publication, String what) {
+ return '$publication is reviewing “$what”';
+ }
@override
- String get authSignIn => '[Šîĝñ îñ one]';
+ String notifSubmissionMessage(String publication, String what) {
+ return '$publication sent a message about “$what”';
+ }
@override
- String get authSignInSubtitle =>
- '[Öñé åççöûñţ ƒöŕ éṽéŕý Ŵöñķý Ŵöŕķ åþþ. one two thr]';
+ String notifSubmissionFailed(String publication, String what) {
+ return '$publication couldn\'t receive “$what”';
+ }
@override
- String get authSignInTitle => '[Šîĝñ îñ one]';
+ String shareModerator(String publication) {
+ return 'Moderator · $publication';
+ }
@override
- String get authSignUpSubtitle =>
- '[Çŕéåţé öñé åççöûñţ ƒöŕ éṽéŕý Ŵöñķý Ŵöŕķ åþþ. one two three]';
+ String activitySubmitted(String who, String publication) {
+ return '$who submitted it to $publication';
+ }
@override
- String get authSignUpTitle => '[Çŕéåţé ýöûŕ åççöûñţ one tw]';
+ String activitySubmissionWithdrawn(String who, String publication) {
+ return '$who withdrew it from $publication';
+ }
@override
- String get authTerms =>
- '[Î åĝŕéé ţö ţĥé ţéŕɱš öƒ šéŕṽîçé åñð þŕîṽåçý þöļîçý one two three f]';
+ String activitySubmissionDisconnected(String who, String publication) {
+ return '$who disconnected it from $publication';
+ }
@override
- String get authTermsLink => '[Ţéŕɱš öƒ šéŕṽîçé one t]';
+ String activitySubmissionReconnected(String who, String publication) {
+ return '$who connected it to $publication again';
+ }
@override
- String get authPrivacyLink => '[Þŕîṽåçý þöļîçý one t]';
+ String activitySubmissionStatus(String publication, String status) {
+ return '$publication: $status';
+ }
@override
- String get authVerify => '[Ṽéŕîƒý one]';
+ String get submissionStatusDraft => 'Draft';
@override
- String get authVerifyNoDraft =>
- '[Šţåŕţ ţĥé šîĝñ-ûþ åĝåîñ ţö ĝéţ å ñéŵ çöðé. one two thre]';
+ String get submissionStatusInReview => 'In review';
@override
- String authVerifySubtitle(String email) {
- return '[Ŵé šéñţ å 6-ðîĝîţ çöðé ţö $email. one two]';
- }
+ String get submissionStatusChangesRequested => 'Changes requested';
@override
- String get authVerifyTitle => '[Ṽéŕîƒý ýöûŕ éɱåîļ one tw]';
+ String get submissionStatusDeclined => 'Declined';
@override
- String get badgeCommentOnly => '[Çöɱɱéñţ öñļý one]';
+ String get submissionStatusPublished => 'Published';
@override
- String get badgeOffline => '[Åṽåîļåƀļé öƒƒļîñé one tw]';
+ String get submissionStatusWithdrawn => 'Withdrawn';
@override
- String get badgeShared => '[Šĥåŕéð one]';
+ String pubStartTitle(String name) {
+ return 'Write for $name';
+ }
@override
- String get badgeViewOnly => '[Ṽîéŵ öñļý one]';
+ String pubStartHostVerified(String host) {
+ return '$host ✓';
+ }
@override
- String get categoryAccount => '[Åççöûñţ one]';
+ String pubStartHostUnverified(String site, String webhook) {
+ return 'Wonky hasn\'t verified that this publication belongs to $site. Your story is sent to $webhook.';
+ }
@override
- String get categoryComments => '[Çöɱɱéñţš one]';
+ String pubStartConsent(String name) {
+ return 'You\'ll write in your own Wonky document. When you press Submit, $name receives the text and images of the document, the fields you fill in and your display name, and its moderators can read and comment on the document.';
+ }
@override
- String get categoryDrive => '[Ðŕîṽé on]';
+ String get pubStartWhich => 'Story';
@override
- String get categoryEdit => '[Éðîţ on]';
+ String pubStartContinue(String name) {
+ return 'Continue “$name”';
+ }
@override
- String get categoryFile => '[Ƒîļé on]';
+ String get pubStartNew => 'Start a new story';
@override
- String get categoryHelp => '[Ĥéļþ on]';
+ String pubStartTerms(String name, String url) {
+ return 'I agree to $name\'s contributor terms ($url)';
+ }
@override
- String get categoryNavigation => '[Ñåṽîĝåţîöñ one]';
+ String pubStartTermsRequired(String name) {
+ return 'Agree to $name\'s contributor terms to start.';
+ }
@override
- String get categoryOther => '[Öţĥéŕ on]';
+ String get pubStartButton => 'Start writing';
@override
- String get categorySharing => '[Šĥåŕîñĝ one]';
+ String pubStartBlocked(String name) {
+ return '$name isn\'t accepting stories from you.';
+ }
@override
- String get categoryTools => '[Ţööļš on]';
+ String pubStartClosed(String name) {
+ return '$name isn\'t accepting stories right now.';
+ }
+}
+
+/// The translations for English (`en_XA`).
+class WonkyAppLocalizationsEnXa extends WonkyAppLocalizationsEn {
+ WonkyAppLocalizationsEnXa() : super('en_XA');
@override
- String get categoryVersions => '[Ṽéŕšîöñ ĥîšţöŕý one t]';
+ String get aboutAppVersion => '[Åþþ ṽéŕšîöñ one]';
@override
- String get categoryView => '[Ṽîéŵ on]';
+ String get aboutLicenses => '[Öþéñ šöûŕçé ļîçéñšéš one two]';
@override
- String get cmdAbout => '[Åƀöûţ on]';
+ String get aboutProtocol => '[Þŕöţöçöļ one]';
@override
- String get cmdAddShortcut => '[Åðð šĥöŕţçûţ ţö Ðŕîṽé one two]';
+ String get aboutRunMode => '[Šéŕṽéŕ ɱöðé one]';
@override
- String cmdAppSettings(String app) {
- return '[$app šéţţîñĝš one]';
- }
+ String get aboutServerVersion => '[Šéŕṽéŕ ṽéŕšîöñ one t]';
@override
- String get cmdAssistant => '[Åššîšţåñţ one]';
+ String get accountMenu => '[Åççöûñţ one]';
@override
- String get cmdAssistantNewThread => '[Ñéŵ åššîšţåñţ çĥåţ one tw]';
+ String get actionBack => '[Ɓåçķ on]';
@override
- String get cmdAssistantSend => '[Šéñð ţö åššîšţåñţ one tw]';
+ String get actionCancel => '[Çåñçéļ one]';
@override
- String get cmdAssistantStop => '[Šţöþ åššîšţåñţ one t]';
+ String get actionClear => '[Çļéåŕ on]';
@override
- String get cmdBack => '[Ɓåçķ on]';
+ String get actionClose => '[Çļöšé on]';
@override
- String get cmdClosePanel => '[Çļöšé þåñéļ one]';
+ String get actionCopy => '[Çöþý on]';
@override
- String get cmdCommandPalette => '[Çöɱɱåñð þåļéţţé one t]';
+ String get actionCreate => '[Çŕéåţé one]';
@override
- String get cmdComment => '[Çöɱɱéñţ one]';
+ String get actionDone => '[Ðöñé on]';
@override
- String get cmdCommentsPanel => '[Çöɱɱéñţš one]';
+ String get actionOk => '[ÖĶ o]';
@override
- String get cmdCopy => '[Çöþý on]';
+ String get actionOpen => '[Öþéñ on]';
@override
- String get cmdCopyLink => '[Çöþý ļîñķ one]';
+ String get actionReload => '[Ŕéļöåð one]';
@override
- String get cmdCopyMcpConfig => '[Çöþý ṀÇÞ çöññéçţîöñ šéţţîñĝš one two t]';
+ String get actionRetry => '[Ŕéţŕý on]';
@override
- String get cmdCut => '[Çûţ on]';
+ String get actionSave => '[Šåṽé on]';
@override
- String get cmdDeleteComment => '[Ðéļéţé çöɱɱéñţ one t]';
+ String get actionSend => '[Šéñð on]';
@override
- String get cmdDeleteForever => '[Ðéļéţé ƒöŕéṽéŕ one t]';
+ String get actionShare => '[Šĥåŕé on]';
@override
- String get cmdDetails => '[Ðéţåîļš one]';
+ String get actionStop => '[Šţöþ on]';
@override
- String get cmdDownload => '[Ðöŵñļöåð one]';
+ String get actionUndo => '[Ûñðö on]';
@override
- String get cmdEmptyTrash => '[Éɱþţý ţŕåšĥ one]';
+ String activityCommented(String who) {
+ return '[$who çöɱɱéñţéð one]';
+ }
@override
- String get cmdFocusSearch => '[Šéåŕçĥ îñ Ðŕîṽé one t]';
+ String activityCreated(String who) {
+ return '[$who çŕéåţéð ţĥîš one]';
+ }
@override
- String get cmdForward => '[Ƒöŕŵåŕð one]';
+ String activityEdited(String who) {
+ return '[$who éðîţéð one]';
+ }
@override
- String get cmdHome => '[Ĥöɱé on]';
+ String get activityEmpty => '[Ñö åçţîṽîţý ýéţ. one t]';
@override
- String get cmdImport => '[Îɱþöŕţ… one]';
+ String activityExported(String who) {
+ return '[$who éẋþöŕţéð one]';
+ }
@override
- String get cmdKeyboardShortcuts => '[Ķéýƀöåŕð šĥöŕţçûţš one tw]';
+ String activityMoved(String who) {
+ return '[$who ɱöṽéð ţĥîš one]';
+ }
@override
- String get cmdMakeAvailableOffline => '[Ṁåķé åṽåîļåƀļé öƒƒļîñé one two]';
+ String activityRenamed(String who) {
+ return '[$who ŕéñåɱéð ţĥîš one]';
+ }
@override
- String get cmdMakeCopy => '[Ṁåķé å çöþý one]';
+ String activityRestored(String who) {
+ return '[$who ŕéšţöŕéð ţĥîš ƒŕöɱ ţĥé ţŕåšĥ one two t]';
+ }
@override
- String get cmdManageStorage => '[Ṁåñåĝé šţöŕåĝé one t]';
+ String activityRestoredVersion(String who) {
+ return '[$who ŕéšţöŕéð å ṽéŕšîöñ one tw]';
+ }
@override
- String get cmdMove => '[Ṁöṽé on]';
+ String activityShared(String who) {
+ return '[$who šĥåŕéð ţĥîš one]';
+ }
@override
- String get cmdMoveToTrash => '[Ṁöṽé ţö ţŕåšĥ one]';
+ String get activitySystem => '[Ŵöñķý Ŵöŕķ one]';
@override
- String get cmdNameCurrentVersion => '[Ñåɱé çûŕŕéñţ ṽéŕšîöñ one two]';
+ String activityTrashed(String who) {
+ return '[$who ɱöṽéð ţĥîš ţö ţĥé ţŕåšĥ one two]';
+ }
@override
- String get cmdNewDocument => '[Ñéŵ ðöçûɱéñţ one]';
+ String activityUnshared(String who) {
+ return '[$who ŕéɱöṽéð åççéšš one t]';
+ }
@override
- String get cmdNewFile => '[Ñéŵ ƒîļé one]';
+ String activityUploaded(String who) {
+ return '[$who ûþļöåðéð ţĥîš one t]';
+ }
@override
- String get cmdNewFolder => '[Ñéŵ ƒöļðéŕ one]';
+ String get addShortcutAction => '[Åðð šĥöŕţçûţ one]';
@override
- String get cmdNewFromTemplate => '[Ƒŕöɱ ţéɱþļåţé… one t]';
+ String get addShortcutTitle => '[Åðð šĥöŕţçûţ ţö Ṁý Ðŕîṽé one two]';
@override
- String get cmdNewSpreadsheet => '[Ñéŵ šþŕéåðšĥééţ one t]';
+ String agentUndoLabel(String label) {
+ return '[ÅÎ: $label on]';
+ }
@override
- String get cmdNextPanel => '[Ñéẋţ þåñéļ one]';
+ String get aiEnable => '[Éñåƀļé ţĥé åššîšţåñţ one two]';
@override
- String get cmdNextRegion => '[Ƒöçûš ñéẋţ ŕéĝîöñ one tw]';
+ String get authBackToSignIn => '[Ɓåçķ ţö šîĝñ îñ one t]';
@override
- String get cmdOpen => '[Öþéñ on]';
+ String get authBackToSignUp => '[Ɓåçķ ţö šîĝñ ûþ one t]';
@override
- String get cmdOpenComment => '[Öþéñ çöɱɱéñţ one]';
+ String get authChangeEmail => '[Çĥåñĝé éɱåîļ åððŕéšš one two]';
@override
- String get cmdOpenFile => '[Öþéñ… on]';
+ String get authCodeResent => '[Å ñéŵ çöðé îš öñ îţš ŵåý. one two]';
@override
- String get cmdOpenInDrive => '[Öþéñ îñ Ŵöñķý Ðŕîṽé one tw]';
+ String get authConfirmPassword => '[Çöñƒîŕɱ þåššŵöŕð one tw]';
@override
- String get cmdOpenPanel => '[Öþéñ þåñéļ one]';
+ String get authCreateAccount => '[Çŕéåţé åççöûñţ one t]';
@override
- String get cmdOpenSheets => '[Öþéñ Ŵöñķý Šĥééţš one tw]';
+ String get authDisplayName => '[Ñåɱé on]';
@override
- String get cmdOpenWrite => '[Öþéñ Ŵöñķý Ŵŕîţé one t]';
+ String get authEmail => '[Éɱåîļ on]';
@override
- String get cmdPaste => '[Þåšţé on]';
+ String get authErrorCodeLength => '[Éñţéŕ ţĥé 6-ðîĝîţ çöðé. one two]';
@override
- String get cmdPastePlain => '[Þåšţé ŵîţĥöûţ ƒöŕɱåţţîñĝ one two]';
+ String get authErrorEmail => '[Éñţéŕ å ṽåļîð éɱåîļ åððŕéšš. one two t]';
@override
- String get cmdPreviousPanel => '[Þŕéṽîöûš þåñéļ one t]';
+ String get authErrorExpiredCode =>
+ '[Ţĥîš çöðé ĥåš éẋþîŕéð. Ŕéǫûéšţ å ñéŵ öñé. one two thre]';
@override
- String get cmdPreviousRegion => '[Ƒöçûš þŕéṽîöûš ŕéĝîöñ one two]';
+ String get authErrorInvalidCode =>
+ '[Ţĥåţ çöðé îš ñöţ ŕîĝĥţ. Çĥéçķ îţ åñð ţŕý åĝåîñ. one two three]';
@override
- String get cmdPrint => '[Þŕîñţ on]';
+ String get authErrorInvalidCredentials =>
+ '[Ŵŕöñĝ éɱåîļ öŕ þåššŵöŕð. one two]';
@override
- String get cmdQuickSwitcher => '[Ĝö ţö ƒîļé… one]';
+ String get authErrorNetwork =>
+ '[Çåñ\'ţ ŕéåçĥ ţĥé šéŕṽéŕ. Çĥéçķ ýöûŕ çöññéçţîöñ åñð ţŕý åĝåîñ. one two three four]';
@override
- String get cmdRedo => '[Ŕéðö on]';
+ String get authErrorPasswordMismatch =>
+ '[Ţĥé þåššŵöŕðš ðöñ\'ţ ɱåţçĥ. one two t]';
@override
- String cmdRedoLabel(String label) {
- return '[Ŕéðö $label on]';
- }
+ String get authErrorPasswordPolicy =>
+ '[Ţĥîš þåššŵöŕð ðöéšñ\'ţ ɱééţ ţĥé ŕéǫûîŕéɱéñţš. Çĥööšé å ļöñĝéŕ öŕ ɱöŕé ṽåŕîéð þåššŵöŕð. one two three four five si]';
@override
- String get cmdRename => '[Ŕéñåɱé one]';
+ String get authErrorPasswordShort => '[Ûšé åţ ļéåšţ 8 çĥåŕåçţéŕš. one two]';
@override
- String get cmdReopen => '[Ŕéöþéñ one]';
+ String get authErrorPasswordWeak =>
+ '[Ţĥîš þåššŵöŕð îš ţöö éåšý ţö ĝûéšš. one two thr]';
@override
- String get cmdReply => '[Ŕéþļý on]';
+ String get authErrorTerms =>
+ '[Þļéåšé åççéþţ ţĥé ţéŕɱš ţö çöñţîñûé. one two thr]';
@override
- String get cmdReportBug => '[Ŕéþöŕţ å ƀûĝ one]';
+ String get authErrorTooManyAttempts =>
+ '[Ţöö ɱåñý åţţéɱþţš. Ŵåîţ å ɱöɱéñţ åñð ţŕý åĝåîñ. one two three]';
@override
- String get cmdReportProblem => '[Ŕéþöŕţ å þŕöƀļéɱ one t]';
+ String get authContinueWithGoogle => '[Çöñţîñûé ŵîţĥ Ĝööĝļé one two]';
@override
- String get cmdRequestAccess => '[Ŕéǫûéšţ åççéšš one t]';
+ String get authOr => '[öŕ o]';
@override
- String get cmdResolve => '[Ŕéšöļṽé one]';
+ String get authGoogleTerms =>
+ '[Ɓý çöñţîñûîñĝ ŵîţĥ Ĝööĝļé, ýöû åĝŕéé ţö ţĥé ţéŕɱš öƒ šéŕṽîçé åñð ţĥé þŕîṽåçý þöļîçý. one two three four five s]';
@override
- String get cmdRestore => '[Ŕéšţöŕé one]';
+ String get authErrorGoogleUnavailable =>
+ '[Šîĝñ îñ ŵîţĥ Ĝööĝļé îšñ\'ţ šéţ ûþ öñ ţĥîš šéŕṽéŕ. one two three]';
@override
- String get cmdRestoreVersion => '[Ŕéšţöŕé ţĥîš ṽéŕšîöñ one two]';
+ String get authErrorPopupBlocked =>
+ '[Ýöûŕ ƀŕöŵšéŕ ƀļöçķéð ţĥé Ĝööĝļé šîĝñ-îñ ŵîñðöŵ. Åļļöŵ þöþ-ûþš ƒöŕ ţĥîš šîţé åñð ţŕý åĝåîñ. one two three four five six]';
@override
- String get cmdSeeVersionHistory => '[Šéé ṽéŕšîöñ ĥîšţöŕý one tw]';
+ String get authErrorUnknown =>
+ '[Šöɱéţĥîñĝ ŵéñţ ŵŕöñĝ. Þļéåšé ţŕý åĝåîñ. one two thre]';
@override
- String get cmdSelectAll => '[Šéļéçţ åļļ one]';
+ String get authForgotPassword => '[Ƒöŕĝöţ þåššŵöŕð? one tw]';
@override
- String get cmdSetDescription => '[Éðîţ ðéšçŕîþţîöñ one tw]';
+ String get authHaveAccount => '[Åļŕéåðý ĥåṽé åñ åççöûñţ? one two]';
@override
- String get cmdSetTheme => '[Åþþ ţĥéɱé one]';
+ String get authHidePassword => '[Ĥîðé þåššŵöŕð one t]';
@override
- String get cmdSettings => '[Šéţţîñĝš one]';
+ String get authNewPassword => '[Ñéŵ þåššŵöŕð one]';
@override
- String get cmdShare => '[Šĥåŕé on]';
+ String get authNewPasswordSubtitle =>
+ '[Çĥööšé å ñéŵ þåššŵöŕð ƒöŕ ýöûŕ åççöûñţ. one two thre]';
@override
- String get cmdSignOut => '[Šîĝñ öûţ one]';
+ String get authNoAccount => '[Ñéŵ ţö Ŵöñķý Ŵöŕķ? one tw]';
@override
- String get cmdStar => '[Šţåŕ on]';
+ String get authPassword => '[Þåššŵöŕð one]';
@override
- String get cmdStopAgent => '[Šţöþ åĝéñţ çöñţŕöļ one tw]';
+ String get authRememberMe => '[Ķééþ ɱé šîĝñéð îñ one t]';
@override
- String get cmdTogglePanel => '[Ţöĝĝļé þåñéļ one]';
+ String get authResendCode => '[Ŕéšéñð çöðé one]';
@override
- String get cmdToggleUiBridge =>
- '[Åļļöŵ ṀÇÞ ţö çöñţŕöļ ţĥîš ŵîñðöŵ one two th]';
+ String authResendIn(int seconds) {
+ return '[Ŕéšéñð çöðé îñ $seconds š one t]';
+ }
@override
- String get cmdUndo => '[Ûñðö on]';
+ String get authResetSubtitle =>
+ '[Ŵé\'ļļ éɱåîļ ýöû å çöðé ţö ŕéšéţ ýöûŕ þåššŵöŕð. one two three]';
@override
- String get cmdUndoHistory => '[Ûñðö ĥîšţöŕý… one t]';
+ String get authResetTitle => '[Ŕéšéţ ýöûŕ þåššŵöŕð one tw]';
@override
- String cmdUndoLabel(String label) {
- return '[Ûñðö $label on]';
- }
+ String get authSendCode => '[Šéñð çöðé one]';
@override
- String get cmdUndoTo => '[Ûñðö ţö šţéþ one]';
+ String get authSetPassword => '[Šéţ þåššŵöŕð åñð šîĝñ îñ one two]';
@override
- String get cmdUnstar => '[Ŕéɱöṽé šţåŕ one]';
+ String get authShowPassword => '[Šĥöŵ þåššŵöŕð one t]';
@override
- String get cmdUpload => '[Ûþļöåð ƒîļé… one]';
+ String get authSignIn => '[Šîĝñ îñ one]';
@override
- String get cmdVersionsPanel => '[Ṽéŕšîöñ ĥîšţöŕý þåñéļ one two]';
+ String get authSignInSubtitle =>
+ '[Öñé åççöûñţ ƒöŕ éṽéŕý Ŵöñķý Ŵöŕķ åþþ. one two thr]';
@override
- String get cmdWhatsNew => '[Ŵĥåţ\'š ñéŵ one]';
+ String get authSignInTitle => '[Šîĝñ îñ one]';
@override
- String get cmdWorkSettings => '[Ŵöñķý Ŵöŕķ šéţţîñĝš one tw]';
+ String get authSignUpSubtitle =>
+ '[Çŕéåţé öñé åççöûñţ ƒöŕ éṽéŕý Ŵöñķý Ŵöŕķ åþþ. one two three]';
@override
- String get cmdZoom => '[Žööɱ on]';
+ String get authSignUpTitle => '[Çŕéåţé ýöûŕ åççöûñţ one tw]';
@override
- String get collapseFolders => '[Çöļļåþšé ƒöļðéŕš one tw]';
+ String get authTerms =>
+ '[Î åĝŕéé ţö ţĥé ţéŕɱš öƒ šéŕṽîçé åñð þŕîṽåçý þöļîçý one two three f]';
@override
- String get columnModified => '[Ļåšţ ɱöðîƒîéð one t]';
+ String get authTermsLink => '[Ţéŕɱš öƒ šéŕṽîçé one t]';
@override
- String get columnName => '[Ñåɱé on]';
+ String get authPrivacyLink => '[Þŕîṽåçý þöļîçý one t]';
@override
- String get columnOwner => '[Öŵñéŕ on]';
+ String get authVerify => '[Ṽéŕîƒý one]';
@override
- String get columnSize => '[Ƒîļé šîžé one]';
+ String get authVerifyNoDraft =>
+ '[Šţåŕţ ţĥé šîĝñ-ûþ åĝåîñ ţö ĝéţ å ñéŵ çöðé. one two thre]';
@override
- String commandFailed(String title) {
- return '[“$title” ƒåîļéð one]';
+ String authVerifySubtitle(String email) {
+ return '[Ŵé šéñţ å 6-ðîĝîţ çöðé ţö $email. one two]';
}
@override
- String get commentAction => '[Çöɱɱéñţ one]';
+ String get authVerifyTitle => '[Ṽéŕîƒý ýöûŕ éɱåîļ one tw]';
@override
- String commentAssign(String name) {
- return '[Åššîĝñ ţö $name one]';
- }
+ String get badgeCommentOnly => '[Çöɱɱéñţ öñļý one]';
@override
- String get commentDelete => '[Ðéļéţé one]';
+ String get badgeOffline => '[Åṽåîļåƀļé öƒƒļîñé one tw]';
@override
- String get commentDeleteThread => '[Ðéļéţé ţĥŕéåð one t]';
+ String get badgeShared => '[Šĥåŕéð one]';
@override
- String get commentDeleted => '[Çöɱɱéñţ ðéļéţéð one t]';
+ String get badgeViewOnly => '[Ṽîéŵ öñļý one]';
@override
- String get commentEdit => '[Éðîţ on]';
+ String get categoryAccount => '[Åççöûñţ one]';
@override
- String get commentEdited => '[éðîţéð one]';
+ String get categoryComments => '[Çöɱɱéñţš one]';
@override
- String get commentEventAssigned => '[åššîĝñéð ţĥé çöɱɱéñţ one two]';
+ String get categoryDrive => '[Ðŕîṽé on]';
@override
- String get commentEventOrphaned =>
- '[ðéļéţéð ţĥé çöɱɱéñţéð çöñţéñţ one two th]';
+ String get categoryEdit => '[Éðîţ on]';
@override
- String get commentEventOrphanedBySystem =>
- '[Ţĥé çöɱɱéñţéð çöñţéñţ ŵåš ðéļéţéð one two thr]';
+ String get categoryFile => '[Ƒîļé on]';
@override
- String get commentEventRestoredBySystem =>
- '[Ţĥé çöɱɱéñţéð çöñţéñţ ŵåš ŕéšţöŕéð one two thr]';
+ String get categoryHelp => '[Ĥéļþ on]';
@override
- String get commentEventReopened => '[ŕéöþéñéð one]';
+ String get categoryNavigation => '[Ñåṽîĝåţîöñ one]';
@override
- String get commentEventResolved => '[ɱåŕķéð åš ŕéšöļṽéð one tw]';
+ String get categoryOther => '[Öţĥéŕ on]';
@override
- String get commentEventSuggestionAccepted =>
- '[åççéþţéð ţĥé šûĝĝéšţîöñ one two]';
+ String get categorySharing => '[Šĥåŕîñĝ one]';
@override
- String get commentEventSuggestionRejected =>
- '[ŕéĵéçţéð ţĥé šûĝĝéšţîöñ one two]';
+ String get categoryTools => '[Ţööļš on]';
@override
- String get commentEventSuggestionReopened =>
- '[ŕéöþéñéð ţĥé šûĝĝéšţîöñ one two]';
+ String get categoryVersions => '[Ṽéŕšîöñ ĥîšţöŕý one t]';
@override
- String get commentEventSuggestionSuperseded =>
- '[ŕéþļåçéð ţĥé šûĝĝéšţîöñ one two]';
+ String get categoryView => '[Ṽîéŵ on]';
@override
- String get commentEventSuggestionWithdrawn =>
- '[ŵîţĥðŕéŵ ţĥé šûĝĝéšţîöñ one two]';
+ String get cmdAbout => '[Åƀöûţ on]';
@override
- String get commentFailed => '[Ţĥé çöɱɱéñţ çöûļð ñöţ ƀé šåṽéð one two t]';
+ String get cmdAddShortcut => '[Åðð šĥöŕţçûţ ţö Ðŕîṽé one two]';
@override
- String get commentPending =>
- '[Þéñðîñĝ — ŵîļļ ƀé þöšţéð ŵĥéñ ýöû\'ŕé öñļîñé one two three]';
+ String cmdAppSettings(String app) {
+ return '[$app šéţţîñĝš one]';
+ }
@override
- String get commentLink => '[Ļîñķ ţö çöɱɱéñţ one t]';
+ String get cmdAssistant => '[Åššîšţåñţ one]';
@override
- String get commentOrphaned => '[Öŕîĝîñåļ çöñţéñţ ðéļéţéð one two]';
+ String get cmdAssistantNewThread => '[Ñéŵ åššîšţåñţ çĥåţ one tw]';
@override
- String get commentPlaceholder => '[Çöɱɱéñţ öŕ åðð öţĥéŕš ŵîţĥ @ one two t]';
+ String get cmdAssistantSend => '[Šéñð ţö åššîšţåñţ one tw]';
@override
- String get commentShow => '[Šĥöŵ on]';
+ String get cmdAssistantStop => '[Šţöþ åššîšţåñţ one t]';
@override
- String commentsEmptyBody(String shortcut) {
- return '[Šéļéçţ çöñţéñţ åñð þŕéšš $shortcut ţö åðð å çöɱɱéñţ. one two three]';
- }
+ String get cmdBack => '[Ɓåçķ on]';
@override
- String get commentsEmptyTitle => '[Ñö çöɱɱéñţš ýéţ one t]';
+ String get cmdClosePanel => '[Çļöšé þåñéļ one]';
@override
- String get commentsForYou => '[Ƒöŕ ýöû one]';
+ String get cmdCommandPalette => '[Çöɱɱåñð þåļéţţé one t]';
@override
- String get commentsOpen => '[Öþéñ on]';
+ String get cmdComment => '[Çöɱɱéñţ one]';
@override
- String get commentsSort => '[Šöŕţ on]';
+ String get cmdCommentsPanel => '[Çöɱɱéñţš one]';
@override
- String get commentsSortDate => '[Šöŕţ ƀý ðåţé one]';
+ String get cmdCopy => '[Çöþý on]';
@override
- String get commentsSortPosition => '[Šöŕţ ƀý þöšîţîöñ one t]';
+ String get cmdCopyLink => '[Çöþý ļîñķ one]';
@override
- String get copyAction => '[Ṁåķé å çöþý one]';
+ String get cmdCopyMcpConfig => '[Çöþý ṀÇÞ çöññéçţîöñ šéţţîñĝš one two t]';
@override
- String get copyComments => '[Çöþý çöɱɱéñţš one t]';
+ String get cmdCut => '[Çûţ on]';
@override
- String get copyName => '[Ñåɱé on]';
+ String get cmdDeleteComment => '[Ðéļéţé çöɱɱéñţ one t]';
@override
- String copyOfName(String name) {
- return '[Çöþý öƒ $name one]';
- }
+ String get cmdDeleteForever => '[Ðéļéţé ƒöŕéṽéŕ one t]';
@override
- String get copySharing => '[Šĥåŕé îţ ŵîţĥ ţĥé šåɱé þéöþļé one two t]';
+ String get cmdDetails => '[Ðéţåîļš one]';
@override
- String get copyTitle => '[Ṁåķé å çöþý one]';
+ String get cmdDownload => '[Ðöŵñļöåð one]';
@override
- String get dateYesterday => '[Ýéšţéŕðåý one]';
+ String get cmdEmptyTrash => '[Éɱþţý ţŕåšĥ one]';
@override
- String get deleteAccountAction => '[Ðéļéţé åççöûñţ one t]';
+ String get cmdFocusSearch => '[Šéåŕçĥ îñ Ðŕîṽé one t]';
@override
- String deleteAccountBody(String email) {
- return '[Ţĥîš þéŕɱåñéñţļý ðéļéţéš ýöûŕ åççöûñţ $email. Ţĥîš çåññöţ ƀé ûñðöñé. one two three four]';
- }
+ String get cmdForward => '[Ƒöŕŵåŕð one]';
@override
- String deleteAccountConfirmLabel(String email) {
- return '[Ţýþé $email ţö çöñƒîŕɱ one t]';
- }
+ String get cmdHome => '[Ĥöɱé on]';
@override
- String deleteAccountCounts(int documents, int spreadsheets, int folders) {
- return '[Ýöû öŵñ $documents ðöçûɱéñţš, $spreadsheets šþŕéåðšĥééţš åñð $folders ƒöļðéŕš. one two three]';
- }
+ String get cmdImport => '[Îɱþöŕţ… one]';
@override
- String get deleteAccountHint =>
- '[Ðéļéţé ýöûŕ åççöûñţ åñð ţĥé ƒîļéš ýöû öŵñ. one two three]';
+ String get cmdKeyboardShortcuts => '[Ķéýƀöåŕð šĥöŕţçûţš one tw]';
@override
- String get deleteAccountMismatch =>
- '[Ţĥåţ\'š ñöţ ýöûŕ éɱåîļ åððŕéšš. one two th]';
+ String get cmdMakeAvailableOffline => '[Ṁåķé åṽåîļåƀļé öƒƒļîñé one two]';
@override
- String get deleteAccountTitle => '[Ðéļéţé åççöûñţ one t]';
+ String get cmdMakeCopy => '[Ṁåķé å çöþý one]';
@override
- String get deleteForeverAction => '[Ðéļéţé ƒöŕéṽéŕ one t]';
+ String get cmdManageStorage => '[Ṁåñåĝé šţöŕåĝé one t]';
@override
- String get deleteForeverBody =>
- '[Ţĥéšé îţéɱš ŵîļļ ƀé ðéļéţéð þéŕɱåñéñţļý. Ţĥîš çåñ\'ţ ƀé ûñðöñé. one two three four]';
+ String get cmdMove => '[Ṁöṽé on]';
@override
- String deleteForeverTitle(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: 'Ðéļéţé $count îţéɱš ƒöŕéṽéŕ?',
- one: 'Ðéļéţé ƒöŕéṽéŕ?',
- );
- return '[$_temp0 one two thre]';
- }
+ String get cmdMoveToTrash => '[Ṁöṽé ţö ţŕåšĥ one]';
@override
- String get densityComfortable => '[Çöɱƒöŕţåƀļé one]';
+ String get cmdNameCurrentVersion => '[Ñåɱé çûŕŕéñţ ṽéŕšîöñ one two]';
@override
- String get densityCompact => '[Çöɱþåçţ one]';
+ String get cmdNewDocument => '[Ñéŵ ðöçûɱéñţ one]';
@override
- String get detailsAccess => '[Ŵĥö ĥåš åççéšš one t]';
+ String get cmdNewFile => '[Ñéŵ ƒîļé one]';
@override
- String get detailsActivity => '[Åçţîṽîţý one]';
+ String get cmdNewFolder => '[Ñéŵ ƒöļðéŕ one]';
@override
- String get detailsCreated => '[Çŕéåţéð one]';
+ String get cmdNewFromTemplate => '[Ƒŕöɱ ţéɱþļåţé… one t]';
@override
- String get detailsDescription => '[Ðéšçŕîþţîöñ one]';
+ String get cmdNewSpreadsheet => '[Ñéŵ šþŕéåðšĥééţ one t]';
@override
- String get detailsDescriptionHint => '[Åðð å ðéšçŕîþţîöñ one tw]';
+ String get cmdNextPanel => '[Ñéẋţ þåñéļ one]';
@override
- String get detailsLocation => '[Ļöçåţîöñ one]';
+ String get cmdNextRegion => '[Ƒöçûš ñéẋţ ŕéĝîöñ one tw]';
@override
- String get detailsModified => '[Ṁöðîƒîéð one]';
+ String get cmdOpen => '[Öþéñ on]';
@override
- String get detailsNothingSelected =>
- '[Šéļéçţ å ƒîļé ţö šéé îţš ðéţåîļš one two th]';
+ String get cmdOpenComment => '[Öþéñ çöɱɱéñţ one]';
@override
- String get detailsOpened => '[Öþéñéð ƀý ɱé one]';
+ String get cmdOpenFile => '[Öþéñ… on]';
@override
- String get detailsOwner => '[Öŵñéŕ on]';
+ String get cmdOpenInDrive => '[Öþéñ îñ Ŵöñķý Ðŕîṽé one tw]';
@override
- String get detailsPrivate => '[Þŕîṽåţé ţö ýöû one t]';
+ String get cmdOpenPanel => '[Öþéñ þåñéļ one]';
@override
- String get detailsRole => '[Ýöûŕ åççéšš one]';
+ String get cmdOpenSheets => '[Öþéñ Ŵöñķý Šĥééţš one tw]';
@override
- String get detailsShared => '[Šĥåŕéð one]';
+ String get cmdOpenWrite => '[Öþéñ Ŵöñķý Ŵŕîţé one t]';
@override
- String get detailsType => '[Ţýþé on]';
+ String get cmdPaste => '[Þåšţé on]';
@override
- String get driveListLabel => '[Ƒîļéš on]';
+ String get cmdPastePlain => '[Þåšţé ŵîţĥöûţ ƒöŕɱåţţîñĝ one two]';
@override
- String get driveOfflineBody =>
- '[Ƒîļéš ýöû öþéñéð ŕéçéñţļý öŕ ɱåðé åṽåîļåƀļé öƒƒļîñé åŕé ļîšţéð ŵĥéñ ýöû\'ŕé öƒƒļîñé. one two three four five s]';
+ String get cmdPreviousPanel => '[Þŕéṽîöûš þåñéļ one t]';
@override
- String get driveOfflineTitle => '[Ýöû\'ŕé öƒƒļîñé one t]';
+ String get cmdPreviousRegion => '[Ƒöçûš þŕéṽîöûš ŕéĝîöñ one two]';
@override
- String get dropToUpload => '[Ðŕöþ ƒîļéš ţö ûþļöåð one tw]';
+ String get cmdPrint => '[Þŕîñţ on]';
@override
- String get editorOfflineTitle =>
- '[Ţĥîš ƒîļé îšñ\'ţ åṽåîļåƀļé öƒƒļîñé one two thr]';
+ String get cmdQuickSwitcher => '[Ĝö ţö ƒîļé… one]';
@override
- String get emptyFolderBody =>
- '[Ðŕöþ ƒîļéš ĥéŕé öŕ ûšé ţĥé Ñéŵ ƀûţţöñ. one two thr]';
+ String get cmdRedo => '[Ŕéðö on]';
@override
- String get emptyFolderTitle => '[Ţĥîš ƒöļðéŕ îš éɱþţý one tw]';
+ String cmdRedoLabel(String label) {
+ return '[Ŕéðö $label on]';
+ }
@override
- String get emptyHomeBody =>
- '[Çŕéåţé å ðöçûɱéñţ öŕ öþéñ öñé šĥåŕéð ŵîţĥ ýöû. one two three]';
+ String get cmdRename => '[Ŕéñåɱé one]';
@override
- String get emptyHomeTitle => '[Ŵéļçöɱé ţö ýöûŕ ðŕîṽé one two]';
+ String get cmdReopen => '[Ŕéöþéñ one]';
@override
- String emptyAppHomeTitle(String appName) {
- return '[Ŵéļçöɱé ţö $appName one]';
- }
+ String get cmdReply => '[Ŕéþļý on]';
@override
- String emptyAppHomeBody(String kind) {
- String _temp0 = intl.Intl.selectLogic(kind, {
- 'spreadsheet': 'Çŕéåţé å šþŕéåðšĥééţ öŕ öþéñ öñé šĥåŕéð ŵîţĥ ýöû.',
- 'presentation': 'Çŕéåţé å þŕéšéñţåţîöñ öŕ öþéñ öñé šĥåŕéð ŵîţĥ ýöû.',
- 'all': 'Çŕéåţé å ðöçûɱéñţ, šþŕéåðšĥééţ öŕ þŕéšéñţåţîöñ, öŕ ûþļöåð ƒîļéš.',
- 'other': 'Çŕéåţé å ðöçûɱéñţ öŕ öþéñ öñé šĥåŕéð ŵîţĥ ýöû.',
- });
- return '[$_temp0 one two three four five six seven eight nine ten one two three]';
- }
+ String get cmdReportBug => '[Ŕéþöŕţ å ƀûĝ one]';
@override
- String get emptyOfflineBody =>
- '[Ûšé “Ṁåķé åṽåîļåƀļé öƒƒļîñé” öñ ƒîļéš ýöû ñééð ŵîţĥöûţ å çöññéçţîöñ. one two three four fi]';
+ String get cmdReportProblem => '[Ŕéþöŕţ å þŕöƀļéɱ one t]';
@override
- String get emptyOfflineTitle => '[Ñö öƒƒļîñé ƒîļéš one t]';
+ String get cmdRequestAccess => '[Ŕéǫûéšţ åççéšš one t]';
@override
- String get emptySearchBody =>
- '[Ţŕý öţĥéŕ ŵöŕðš öŕ ƒéŵéŕ ƒîļţéŕš. one two th]';
+ String get cmdResolve => '[Ŕéšöļṽé one]';
@override
- String get emptySearchTitle => '[Ñö ŕéšûļţš one]';
+ String get cmdRestore => '[Ŕéšţöŕé one]';
@override
- String get emptySharedBody =>
- '[Ƒîļéš öţĥéŕš šĥåŕé ŵîţĥ ýöû ŵîļļ šĥöŵ ûþ ĥéŕé. one two three]';
+ String get cmdRestoreVersion => '[Ŕéšţöŕé ţĥîš ṽéŕšîöñ one two]';
@override
- String get emptySharedTitle => '[Ñöţĥîñĝ šĥåŕéð ŵîţĥ ýöû ýéţ one two t]';
+ String get cmdSeeVersionHistory => '[Šéé ṽéŕšîöñ ĥîšţöŕý one tw]';
@override
- String get emptyStarredBody =>
- '[Åðð šţåŕš ţö ţĥîñĝš ýöû ŵåñţ ţö ƒîñð éåšîļý ļåţéŕ. one two three f]';
+ String get cmdSelectAll => '[Šéļéçţ åļļ one]';
@override
- String get emptyStarredTitle => '[Ñö šţåŕŕéð ƒîļéš ýéţ one tw]';
+ String get cmdSetDescription => '[Éðîţ ðéšçŕîþţîöñ one tw]';
@override
- String get emptyTrashAction => '[Éɱþţý ţŕåšĥ one]';
+ String get cmdSetTheme => '[Åþþ ţĥéɱé one]';
@override
- String get emptyTrashBody =>
- '[Åļļ îţéɱš îñ ţĥé ţŕåšĥ ŵîļļ ƀé ðéļéţéð ƒöŕéṽéŕ. Ţĥîš çåñ\'ţ ƀé ûñðöñé. one two three four f]';
+ String get cmdSettings => '[Šéţţîñĝš one]';
@override
- String get emptyTrashTitle => '[Éɱþţý ţŕåšĥ? one]';
+ String get cmdShare => '[Šĥåŕé on]';
@override
- String get emptyTrashViewBody =>
- '[Îţéɱš îñ ţĥé ţŕåšĥ åŕé ðéļéţéð ƒöŕéṽéŕ åƒţéŕ 30 ðåýš. one two three fo]';
+ String get cmdSignOut => '[Šîĝñ öûţ one]';
@override
- String get emptyTrashViewTitle => '[Ţŕåšĥ îš éɱþţý one t]';
+ String get cmdStar => '[Šţåŕ on]';
@override
- String get errorBoundaryBody =>
- '[Ţĥîš þåŕţ öƒ ţĥé åþþ ĥîţ åñ ûñéẋþéçţéð éŕŕöŕ. Ýöûŕ ŵöŕķ îš šåṽéð. one two three four]';
+ String get cmdStopAgent => '[Šţöþ åĝéñţ çöñţŕöļ one tw]';
@override
- String get errorBoundaryTitle => '[Šöɱéţĥîñĝ ŵéñţ ŵŕöñĝ one two]';
+ String get cmdTogglePanel => '[Ţöĝĝļé þåñéļ one]';
@override
- String get errorCopyDetails => '[Çöþý éŕŕöŕ ðéţåîļš one tw]';
+ String get cmdToggleUiBridge =>
+ '[Åļļöŵ ṀÇÞ ţö çöñţŕöļ ţĥîš ŵîñðöŵ one two th]';
@override
- String get errorGeneric => '[Šöɱéţĥîñĝ ŵéñţ ŵŕöñĝ one two]';
+ String get cmdUndo => '[Ûñðö on]';
@override
- String get errorNameSlash => '[Ñåɱéš çåñ\'ţ çöñţåîñ “/”. one two]';
+ String get cmdUndoHistory => '[Ûñðö ĥîšţöŕý… one t]';
@override
- String get errorNameTooLong => '[Ûšé åţ ɱöšţ 255 çĥåŕåçţéŕš. one two t]';
+ String cmdUndoLabel(String label) {
+ return '[Ûñðö $label on]';
+ }
@override
- String get errorRequired => '[Ŕéǫûîŕéð one]';
+ String get cmdUndoTo => '[Ûñðö ţö šţéþ one]';
@override
- String get expandFolders => '[Éẋþåñð ƒöļðéŕš one t]';
+ String get cmdUnstar => '[Ŕéɱöṽé šţåŕ one]';
@override
- String get externalLinkOpen => '[Öþéñ on]';
+ String get cmdUpload => '[Ûþļöåð ƒîļé… one]';
@override
- String get externalLinkTitle => '[Öþéñ éẋţéŕñåļ ļîñķ? one tw]';
+ String get cmdVersionsPanel => '[Ṽéŕšîöñ ĥîšţöŕý þåñéļ one two]';
@override
- String get fileCategoryArchives => '[Åŕçĥîṽéš one]';
+ String get cmdWhatsNew => '[Ŵĥåţ\'š ñéŵ one]';
@override
- String get fileCategoryAudio => '[Åûðîö on]';
+ String get cmdWorkSettings => '[Ŵöñķý Ŵöŕķ šéţţîñĝš one tw]';
@override
- String get fileCategoryImages => '[Îɱåĝéš one]';
+ String get cmdZoom => '[Žööɱ on]';
@override
- String get fileCategoryOther => '[Öţĥéŕ ƒîļéš one]';
+ String get collapseFolders => '[Çöļļåþšé ƒöļðéŕš one tw]';
@override
- String get fileCategoryPdfs => '[ÞÐƑš on]';
+ String get columnModified => '[Ļåšţ ɱöðîƒîéð one t]';
@override
- String get fileCategoryText => '[Ţéẋţ & çöðé one]';
+ String get columnName => '[Ñåɱé on]';
@override
- String get fileCategoryVideo => '[Ṽîðéöš one]';
+ String get columnOwner => '[Öŵñéŕ on]';
@override
- String get folderTree => '[Ƒöļðéŕš one]';
+ String get columnSize => '[Ƒîļé šîžé one]';
@override
- String get foldersOnTop => '[Ƒöļðéŕš öñ ţöþ one t]';
+ String commandFailed(String title) {
+ return '[“$title” ƒåîļéð one]';
+ }
@override
- String get goHome => '[Ĝö ţö Ĥöɱé one]';
+ String get commentAction => '[Çöɱɱéñţ one]';
@override
- String get groupEarlier => '[Éåŕļîéŕ one]';
+ String commentAssign(String name) {
+ return '[Åššîĝñ ţö $name one]';
+ }
@override
- String get groupThisWeek => '[Éåŕļîéŕ ţĥîš ŵééķ one tw]';
+ String get commentDelete => '[Ðéļéţé one]';
@override
- String get groupToday => '[Ţöðåý on]';
+ String get commentDeleteThread => '[Ðéļéţé ţĥŕéåð one t]';
@override
- String get groupYesterday => '[Ýéšţéŕðåý one]';
+ String get commentDeleted => '[Çöɱɱéñţ ðéļéţéð one t]';
@override
- String get historyBack => '[Ɓåçķ ţö éðîţîñĝ one t]';
+ String get commentEdit => '[Éðîţ on]';
@override
- String get historyNoRenderer =>
- '[Þŕéṽîéŵš öƒ ṽéŕšîöñš åŕéñ\'ţ åṽåîļåƀļé ƒöŕ ţĥîš ƒîļé ţýþé. one two three four]';
+ String get commentEdited => '[éðîţéð one]';
@override
- String get historyRestoreThis => '[Ŕéšţöŕé ţĥîš ṽéŕšîöñ one two]';
+ String get commentEventAssigned => '[åššîĝñéð ţĥé çöɱɱéñţ one two]';
@override
- String get historyShowChanges => '[Šĥöŵ çĥåñĝéš one]';
+ String get commentEventOrphaned =>
+ '[ðéļéţéð ţĥé çöɱɱéñţéð çöñţéñţ one two th]';
@override
- String get importFailed => '[Ţĥé îɱþöŕţ ƒåîļéð one tw]';
+ String get commentEventOrphanedBySystem =>
+ '[Ţĥé çöɱɱéñţéð çöñţéñţ ŵåš ðéļéţéð one two thr]';
@override
- String get importUnsupported =>
- '[Ţĥéšé ƒîļéš çåñ\'ţ ƀé îɱþöŕţéð ĥéŕé one two thr]';
+ String get commentEventRestoredBySystem =>
+ '[Ţĥé çöɱɱéñţéð çöñţéñţ ŵåš ŕéšţöŕéð one two thr]';
@override
- String get inviteInvalid => '[Ţĥîš ļîñķ îš ñöţ ṽåļîð one two]';
+ String get commentEventReopened => '[ŕéöþéñéð one]';
@override
- String get inviteTitle => '[Öþéñîñĝ šĥåŕéð ƒîļé one tw]';
+ String get commentEventResolved => '[ɱåŕķéð åš ŕéšöļṽéð one tw]';
@override
- String get kindDocument => '[Ðöçûɱéñţ one]';
+ String get commentEventSuggestionAccepted =>
+ '[åççéþţéð ţĥé šûĝĝéšţîöñ one two]';
@override
- String get kindFile => '[Ƒîļé on]';
+ String get commentEventSuggestionRejected =>
+ '[ŕéĵéçţéð ţĥé šûĝĝéšţîöñ one two]';
@override
- String get kindFolder => '[Ƒöļðéŕ one]';
+ String get commentEventSuggestionReopened =>
+ '[ŕéöþéñéð ţĥé šûĝĝéšţîöñ one two]';
@override
- String get kindPresentation => '[Þŕéšéñţåţîöñ one t]';
+ String get commentEventSuggestionSuperseded =>
+ '[ŕéþļåçéð ţĥé šûĝĝéšţîöñ one two]';
@override
- String get kindSpreadsheet => '[Šþŕéåðšĥééţ one]';
-
- @override
- String get layoutGrid => '[Ĝŕîð ṽîéŵ one]';
+ String get commentEventSuggestionWithdrawn =>
+ '[ŵîţĥðŕéŵ ţĥé šûĝĝéšţîöñ one two]';
@override
- String get layoutList => '[Ļîšţ ṽîéŵ one]';
+ String get commentFailed => '[Ţĥé çöɱɱéñţ çöûļð ñöţ ƀé šåṽéð one two t]';
@override
- String get mcpAllowControl => '[Åļļöŵ ṀÇÞ ţö çöñţŕöļ ţĥîš ŵîñðöŵ one two th]';
+ String get commentPending =>
+ '[Þéñðîñĝ — ŵîļļ ƀé þöšţéð ŵĥéñ ýöû\'ŕé öñļîñé one two three]';
@override
- String get mcpControlling =>
- '[Åñ åĝéñţ îš çöñţŕöļļîñĝ ţĥîš ŵîñðöŵ one two thr]';
+ String get commentLink => '[Ļîñķ ţö çöɱɱéñţ one t]';
@override
- String get mcpHint =>
- '[Ûšé ţĥîš çöñƒîĝûŕåţîöñ îñ Çļåûðé Ðéšķţöþ öŕ Çļåûðé Çöðé ŵîţĥ öñé öƒ ýöûŕ ţöķéñš. one two three four five]';
+ String get commentOrphaned => '[Öŕîĝîñåļ çöñţéñţ ðéļéţéð one two]';
@override
- String get mcpTitle => '[ṀÇÞ çöññéçţîöñ one t]';
+ String get commentPlaceholder => '[Çöɱɱéñţ öŕ åðð öţĥéŕš ŵîţĥ @ one two t]';
@override
- String get menuAccessibility => '[Åççéššîƀîļîţý šéţţîñĝš one two]';
+ String get commentShow => '[Šĥöŵ on]';
@override
- String get menuAppTheme => '[Åþþ ţĥéɱé one]';
+ String commentsEmptyBody(String shortcut) {
+ return '[Šéļéçţ çöñţéñţ åñð þŕéšš $shortcut ţö åðð å çöɱɱéñţ. one two three]';
+ }
@override
- String get menuDownload => '[Ðöŵñļöåð one]';
+ String get commentsEmptyTitle => '[Ñö çöɱɱéñţš ýéţ one t]';
@override
- String get menuEdit => '[Éðîţ on]';
+ String get commentsForYou => '[Ƒöŕ ýöû one]';
@override
- String get menuExplore => '[Éẋþļöŕé one]';
+ String get commentsOpen => '[Öþéñ on]';
@override
- String get menuFile => '[Ƒîļé on]';
+ String get commentsSort => '[Šöŕţ on]';
@override
- String get menuFileUpload => '[Ƒîļé ûþļöåð one]';
+ String get commentsSortDate => '[Šöŕţ ƀý ðåţé one]';
@override
- String get menuHelp => '[Ĥéļþ on]';
+ String get commentsSortPosition => '[Šöŕţ ƀý þöšîţîöñ one t]';
@override
- String get menuImportFile => '[Îɱþöŕţ ƒîļé… one]';
+ String get copyAction => '[Ṁåķé å çöþý one]';
@override
- String get menuMakeCopy => '[Ṁåķé å çöþý one]';
+ String get copyComments => '[Çöþý çöɱɱéñţš one t]';
@override
- String get menuMode => '[Ṁöðé on]';
+ String get copyName => '[Ñåɱé on]';
@override
- String get menuNew => '[Ñéŵ on]';
+ String copyOfName(String name) {
+ return '[Çöþý öƒ $name one]';
+ }
@override
- String get menuNewDocument => '[Ðöçûɱéñţ one]';
+ String get copySharing => '[Šĥåŕé îţ ŵîţĥ ţĥé šåɱé þéöþļé one two t]';
@override
- String get menuNewPresentation => '[Þŕéšéñţåţîöñ one t]';
+ String get copyTitle => '[Ṁåķé å çöþý one]';
@override
- String get menuNewSpreadsheet => '[Šþŕéåðšĥééţ one]';
+ String get dateYesterday => '[Ýéšţéŕðåý one]';
@override
- String get menuOpenApp => '[Öþéñ on]';
+ String get deleteAccountAction => '[Ðéļéţé åççöûñţ one t]';
@override
- String get menuPanels => '[Þåñéļš one]';
+ String deleteAccountBody(String email) {
+ return '[Ţĥîš þéŕɱåñéñţļý ðéļéţéš ýöûŕ åççöûñţ $email. Ţĥîš çåññöţ ƀé ûñðöñé. one two three four]';
+ }
@override
- String get menuPreferences => '[Þŕéƒéŕéñçéš one]';
+ String deleteAccountConfirmLabel(String email) {
+ return '[Ţýþé $email ţö çöñƒîŕɱ one t]';
+ }
@override
- String get menuShare => '[Šĥåŕé on]';
+ String deleteAccountCounts(int documents, int spreadsheets, int folders) {
+ return '[Ýöû öŵñ $documents ðöçûɱéñţš, $spreadsheets šþŕéåðšĥééţš åñð $folders ƒöļðéŕš. one two three]';
+ }
@override
- String get menuTools => '[Ţööļš on]';
+ String get deleteAccountHint =>
+ '[Ðéļéţé ýöûŕ åççöûñţ åñð ţĥé ƒîļéš ýöû öŵñ. one two three]';
@override
- String get menuVersionHistory => '[Ṽéŕšîöñ ĥîšţöŕý one t]';
+ String get deleteAccountMismatch =>
+ '[Ţĥåţ\'š ñöţ ýöûŕ éɱåîļ åððŕéšš. one two th]';
@override
- String get menuView => '[Ṽîéŵ on]';
+ String get deleteAccountTitle => '[Ðéļéţé åççöûñţ one t]';
@override
- String get menuZoom => '[Žööɱ on]';
+ String get deleteForeverAction => '[Ðéļéţé ƒöŕéṽéŕ one t]';
@override
- String get menus => '[Ṁéñûš on]';
+ String get deleteForeverBody =>
+ '[Ţĥéšé îţéɱš ŵîļļ ƀé ðéļéţéð þéŕɱåñéñţļý. Ţĥîš çåñ\'ţ ƀé ûñðöñé. one two three four]';
@override
- String get moreActions => '[Ṁöŕé åçţîöñš one]';
+ String deleteForeverTitle(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'Ðéļéţé $count îţéɱš ƒöŕéṽéŕ?',
+ one: 'Ðéļéţé ƒöŕéṽéŕ?',
+ );
+ return '[$_temp0 one two thre]';
+ }
@override
- String get moveHere => '[Ṁöṽé ĥéŕé one]';
+ String get densityComfortable => '[Çöɱƒöŕţåƀļé one]';
@override
- String get moveNoFolders => '[Ñö ƒöļðéŕš ĥéŕé one t]';
+ String get densityCompact => '[Çöɱþåçţ one]';
@override
- String get moveNoPermission =>
- '[Ýöû ðöñ\'ţ ĥåṽé þéŕɱîššîöñ ţö åðð îţéɱš ţö ţĥîš ƒöļðéŕ. one two three fo]';
+ String get detailsAccess => '[Ŵĥö ĥåš åççéšš one t]';
@override
- String moveTitleMany(int count) {
- return '[Ṁöṽé $count îţéɱš one]';
- }
+ String get detailsActivity => '[Åçţîṽîţý one]';
@override
- String moveTitleOne(String name) {
- return '[Ṁöṽé “$name” one]';
- }
+ String get detailsCreated => '[Çŕéåţéð one]';
@override
- String get nameVersionLabel => '[Ṽéŕšîöñ ñåɱé one]';
+ String get detailsDescription => '[Ðéšçŕîþţîöñ one]';
@override
- String get nameVersionTitle => '[Ñåɱé ţĥîš ṽéŕšîöñ one tw]';
+ String get detailsDescriptionHint => '[Åðð å ðéšçŕîþţîöñ one tw]';
@override
- String get navApps => '[Åþþš on]';
+ String get detailsLocation => '[Ļöçåţîöñ one]';
@override
- String get navHome => '[Ĥöɱé on]';
+ String get detailsModified => '[Ṁöðîƒîéð one]';
@override
- String get navMyDrive => '[Ṁý Ðŕîṽé one]';
+ String get detailsNothingSelected =>
+ '[Šéļéçţ å ƒîļé ţö šéé îţš ðéţåîļš one two th]';
@override
- String get navMyFiles => '[Ṁý ƒîļéš one]';
+ String get detailsOpened => '[Öþéñéð ƀý ɱé one]';
@override
- String get navOffline => '[Öƒƒļîñé one]';
+ String get detailsOwner => '[Öŵñéŕ on]';
@override
- String get navRecent => '[Ŕéçéñţ one]';
+ String get detailsPrivate => '[Þŕîṽåţé ţö ýöû one t]';
@override
- String get navShared => '[Šĥåŕéð ŵîţĥ ɱé one t]';
+ String get detailsRole => '[Ýöûŕ åççéšš one]';
@override
- String get navStarred => '[Šţåŕŕéð one]';
+ String get detailsShared => '[Šĥåŕéð one]';
@override
- String get navStorage => '[Šţöŕåĝé one]';
+ String get detailsType => '[Ţýþé on]';
@override
- String get navTrash => '[Ţŕåšĥ on]';
+ String get driveListLabel => '[Ƒîļéš on]';
@override
- String get newButton => '[Ñéŵ on]';
+ String get driveOfflineBody =>
+ '[Ƒîļéš ýöû öþéñéð ŕéçéñţļý öŕ ɱåðé åṽåîļåƀļé öƒƒļîñé åŕé ļîšţéð ŵĥéñ ýöû\'ŕé öƒƒļîñé. one two three four five s]';
@override
- String get newFolderLabel => '[Ƒöļðéŕ ñåɱé one]';
+ String get driveOfflineTitle => '[Ýöû\'ŕé öƒƒļîñé one t]';
@override
- String get newFolderTitle => '[Ñéŵ ƒöļðéŕ one]';
+ String get dropToUpload => '[Ðŕöþ ƒîļéš ţö ûþļöåð one tw]';
@override
- String noAccessBody(String email) {
- return '[Ýöû ñééð åççéšš ţö öþéñ ţĥîš ƒîļé. Ýöû\'ŕé šîĝñéð îñ åš $email. one two three fo]';
- }
+ String get editorOfflineTitle =>
+ '[Ţĥîš ƒîļé îšñ\'ţ åṽåîļåƀļé öƒƒļîñé one two thr]';
@override
- String get noAccessTitle => '[Ýöû ñééð åççéšš one t]';
+ String get emptyFolderBody =>
+ '[Ðŕöþ ƒîļéš ĥéŕé öŕ ûšé ţĥé Ñéŵ ƀûţţöñ. one two thr]';
@override
- String get notAllowed => '[Ñöţ åļļöŵéð one]';
+ String get emptyFolderTitle => '[Ţĥîš ƒöļðéŕ îš éɱþţý one tw]';
@override
- String get notFoundBody =>
- '[Ţĥé þåĝé ýöû\'ŕé ļööķîñĝ ƒöŕ ðöéšñ\'ţ éẋîšţ. one two three]';
+ String get emptyHomeBody =>
+ '[Çŕéåţé å ðöçûɱéñţ öŕ öþéñ öñé šĥåŕéð ŵîţĥ ýöû. one two three]';
@override
- String get notFoundTitle => '[Þåĝé ñöţ ƒöûñð one t]';
+ String get emptyHomeTitle => '[Ŵéļçöɱé ţö ýöûŕ ðŕîṽé one two]';
@override
- String notifAccessRequest(String who, String item) {
- return '[$who åšķéð ƒöŕ åççéšš ţö $item one tw]';
+ String emptyAppHomeTitle(String appName) {
+ return '[Ŵéļçöɱé ţö $appName one]';
}
@override
- String notifAssigned(String who, String item) {
- return '[$who åššîĝñéð ýöû å çöɱɱéñţ îñ $item one two]';
+ String emptyAppHomeBody(String kind) {
+ String _temp0 = intl.Intl.selectLogic(kind, {
+ 'spreadsheet': 'Çŕéåţé å šþŕéåðšĥééţ öŕ öþéñ öñé šĥåŕéð ŵîţĥ ýöû.',
+ 'presentation': 'Çŕéåţé å þŕéšéñţåţîöñ öŕ öþéñ öñé šĥåŕéð ŵîţĥ ýöû.',
+ 'all': 'Çŕéåţé å ðöçûɱéñţ, šþŕéåðšĥééţ öŕ þŕéšéñţåţîöñ, öŕ ûþļöåð ƒîļéš.',
+ 'other': 'Çŕéåţé å ðöçûɱéñţ öŕ öþéñ öñé šĥåŕéð ŵîţĥ ýöû.',
+ });
+ return '[$_temp0 one two three four five six seven eight nine ten one two three]';
}
@override
- String notifMention(String who, String item) {
- return '[$who ɱéñţîöñéð ýöû îñ $item one t]';
- }
+ String get emptyOfflineBody =>
+ '[Ûšé “Ṁåķé åṽåîļåƀļé öƒƒļîñé” öñ ƒîļéš ýöû ñééð ŵîţĥöûţ å çöññéçţîöñ. one two three four fi]';
@override
- String notifOwnership(String who, String item) {
- return '[$who ŵåñţš ţö ɱåķé ýöû ţĥé öŵñéŕ öƒ $item one two t]';
- }
+ String get emptyOfflineTitle => '[Ñö öƒƒļîñé ƒîļéš one t]';
@override
- String notifReply(String who, String item) {
- return '[$who ŕéþļîéð îñ $item one]';
- }
+ String get emptySearchBody =>
+ '[Ţŕý öţĥéŕ ŵöŕðš öŕ ƒéŵéŕ ƒîļţéŕš. one two th]';
@override
- String notifScriptFailure(String item) {
- return '[Å šçŕîþţ ƒåîļéð îñ $item one tw]';
- }
+ String get emptySearchTitle => '[Ñö ŕéšûļţš one]';
@override
- String notifShared(String who, String item) {
- return '[$who šĥåŕéð $item ŵîţĥ ýöû one t]';
- }
+ String get emptySharedBody =>
+ '[Ƒîļéš öţĥéŕš šĥåŕé ŵîţĥ ýöû ŵîļļ šĥöŵ ûþ ĥéŕé. one two three]';
@override
- String get notifStorage =>
- '[Ýöûŕ Ŵöñķý Ŵöŕķ šţöŕåĝé ñééðš åţţéñţîöñ one two thre]';
+ String get emptySharedTitle => '[Ñöţĥîñĝ šĥåŕéð ŵîţĥ ýöû ýéţ one two t]';
@override
- String notifBoardReply(String who, String title) {
- return '[$who çöɱɱéñţéð öñ “$title” one t]';
- }
+ String get emptyStarredBody =>
+ '[Åðð šţåŕš ţö ţĥîñĝš ýöû ŵåñţ ţö ƒîñð éåšîļý ļåţéŕ. one two three f]';
@override
- String notifBoardUpdate(String title) {
- return '[Ûþðåţé öñ “$title” one]';
- }
+ String get emptyStarredTitle => '[Ñö šţåŕŕéð ƒîļéš ýéţ one tw]';
@override
- String notifSuggestion(String who, String item) {
- return '[$who šûĝĝéšţéð åñ éðîţ îñ $item one tw]';
- }
+ String get emptyTrashAction => '[Éɱþţý ţŕåšĥ one]';
@override
- String notifSuggestionDecided(String who, String item) {
- return '[$who ŕéṽîéŵéð ýöûŕ šûĝĝéšţîöñ îñ $item one two t]';
- }
+ String get emptyTrashBody =>
+ '[Åļļ îţéɱš îñ ţĥé ţŕåšĥ ŵîļļ ƀé ðéļéţéð ƒöŕéṽéŕ. Ţĥîš çåñ\'ţ ƀé ûñðöñé. one two three four f]';
@override
- String get notificationsEmpty => '[Ýöû\'ŕé åļļ çåûĝĥţ ûþ one tw]';
+ String get emptyTrashTitle => '[Éɱþţý ţŕåšĥ? one]';
@override
- String get notificationsMarkAllRead => '[Ṁåŕķ åļļ åš ŕéåð one t]';
+ String get emptyTrashViewBody =>
+ '[Îţéɱš îñ ţĥé ţŕåšĥ åŕé ðéļéţéð ƒöŕéṽéŕ åƒţéŕ 30 ðåýš. one two three fo]';
@override
- String get notificationsTitle => '[Ñöţîƒîçåţîöñš one t]';
+ String get emptyTrashViewTitle => '[Ţŕåšĥ îš éɱþţý one t]';
@override
- String notificationsUnread(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '$count ûñŕéåð ñöţîƒîçåţîöñš',
- one: '1 ûñŕéåð ñöţîƒîçåţîöñ',
- );
- return '[$_temp0 one two three]';
- }
+ String get errorBoundaryBody =>
+ '[Ţĥîš þåŕţ öƒ ţĥé åþþ ĥîţ åñ ûñéẋþéçţéð éŕŕöŕ. Ýöûŕ ŵöŕķ îš šåṽéð. one two three four]';
@override
- String get notifyEmail => '[Îñ-åþþ åñð éɱåîļ one t]';
+ String get errorBoundaryTitle => '[Šöɱéţĥîñĝ ŵéñţ ŵŕöñĝ one two]';
@override
- String get notifyInApp => '[Îñ-åþþ one]';
+ String get errorCopyDetails => '[Çöþý éŕŕöŕ ðéţåîļš one tw]';
@override
- String get notifyMentions => '[Ṁéñţîöñš one]';
+ String get errorGeneric => '[Šöɱéţĥîñĝ ŵéñţ ŵŕöñĝ one two]';
@override
- String get notifyOff => '[Öƒƒ on]';
+ String get errorNameSlash => '[Ñåɱéš çåñ\'ţ çöñţåîñ “/”. one two]';
@override
- String get notifyReplies => '[Ŕéþļîéš ţö ɱý çöɱɱéñţš one two]';
+ String get errorNameTooLong => '[Ûšé åţ ɱöšţ 255 çĥåŕåçţéŕš. one two t]';
@override
- String get notifyShares => '[Ƒîļéš šĥåŕéð ŵîţĥ ɱé one tw]';
+ String get errorRequired => '[Ŕéǫûîŕéð one]';
@override
- String get offlineBanner =>
- '[Ýöû\'ŕé öƒƒļîñé. Çĥåñĝéš åŕé šåṽéð öñ ţĥîš ðéṽîçé åñð ŵîļļ šýñç ŵĥéñ ýöû\'ŕé ƀåçķ öñļîñé. one two three four five si]';
+ String get expandFolders => '[Éẋþåñð ƒöļðéŕš one t]';
@override
- String get offlineClear => '[Çļéåŕ ļöçåļ ðåţå one t]';
+ String get externalLinkOpen => '[Öþéñ on]';
@override
- String get offlineClearHint =>
- '[Ŕéɱöṽéš çåçĥéð ƒîļéš åñð þéñðîñĝ çĥåñĝéš ƒŕöɱ ţĥîš ðéṽîçé. one two three four]';
+ String get externalLinkTitle => '[Öþéñ éẋţéŕñåļ ļîñķ? one tw]';
@override
- String get offlineCleared => '[Ļöçåļ ðåţå çļéåŕéð one tw]';
+ String get fileCategoryArchives => '[Åŕçĥîṽéš one]';
@override
- String get offlinePending => '[Çĥåñĝéš ŵåîţîñĝ ţö šýñç one two]';
+ String get fileCategoryAudio => '[Åûðîö on]';
@override
- String get badgeOfflineRecent =>
- '[Åṽåîļåƀļé öƒƒļîñé (öþéñéð ŕéçéñţļý) one two thre]';
+ String get fileCategoryImages => '[Îɱåĝéš one]';
@override
- String get badgeOfflinePending => '[Ðöŵñļöåðîñĝ ƒöŕ öƒƒļîñé ûšé… one two t]';
+ String get fileCategoryOther => '[Öţĥéŕ ƒîļéš one]';
@override
- String get offlineUnavailable =>
- '[Ţĥîš ƒîļé îšñ\'ţ åṽåîļåƀļé öƒƒļîñé one two thr]';
+ String get fileCategoryPdfs => '[ÞÐƑš on]';
@override
- String get offlineDocuments => '[Ƒîļéš öñ ţĥîš ðéṽîçé one tw]';
+ String get fileCategoryText => '[Ţéẋţ & çöðé one]';
@override
- String offlineDocumentsValue(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '$count ƒîļéš',
- one: '1 ƒîļé',
- zero: 'Ñöñé',
- );
- return '[$_temp0 one t]';
- }
+ String get fileCategoryVideo => '[Ṽîðéöš one]';
@override
- String offlinePinnedCount(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '$count ɱåðé åṽåîļåƀļé öƒƒļîñé',
- one: '1 ɱåðé åṽåîļåƀļé öƒƒļîñé',
- zero: 'Ñö ƒîļéš ɱåðé åṽåîļåƀļé öƒƒļîñé',
- );
- return '[$_temp0 one two three four five]';
- }
+ String get folderTree => '[Ƒöļðéŕš one]';
@override
- String get offlineUploads => '[Îɱåĝéš ŵåîţîñĝ ţö ûþļöåð one two]';
+ String get foldersOnTop => '[Ƒöļðéŕš öñ ţöþ one t]';
@override
- String get offlineStorageUsed => '[Šţöŕåĝé ûšéð one]';
+ String get goHome => '[Ĝö ţö Ĥöɱé one]';
@override
- String offlineClearUnsynced(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '$count çĥåñĝéš ĥåṽéñ\'ţ šýñçéð ýéţ åñð ŵîļļ ƀé ļöšţ.',
- one: '1 çĥåñĝé ĥåšñ\'ţ šýñçéð ýéţ åñð ŵîļļ ƀé ļöšţ.',
- );
- return '[$_temp0 one two three four five si]';
- }
+ String get groupEarlier => '[Éåŕļîéŕ one]';
@override
- String get offlineSimulate => '[Šîɱûļåţé öƒƒļîñé one tw]';
+ String get groupThisWeek => '[Éåŕļîéŕ ţĥîš ŵééķ one tw]';
@override
- String get offlineSimulateHint =>
- '[Ðîšçöññéçţš ƒŕöɱ ţĥé šéŕṽéŕ ţö ţŕý öƒƒļîñé éðîţîñĝ (ðéṽéļöþéŕ šéţţîñĝ). one two three four fiv]';
+ String get groupToday => '[Ţöðåý on]';
@override
- String get ownerAnyone => '[Öŵñéð ƀý åñýöñé one t]';
+ String get groupYesterday => '[Ýéšţéŕðåý one]';
@override
- String get ownerMe => '[ɱé o]';
+ String get historyBack => '[Ɓåçķ ţö éðîţîñĝ one t]';
@override
- String get ownerMeFilter => '[Öŵñéð ƀý ɱé one]';
+ String get historyNoRenderer =>
+ '[Þŕéṽîéŵš öƒ ṽéŕšîöñš åŕéñ\'ţ åṽåîļåƀļé ƒöŕ ţĥîš ƒîļé ţýþé. one two three four]';
@override
- String get ownerNotMe => '[Ñöţ öŵñéð ƀý ɱé one t]';
+ String get historyRestoreThis => '[Ŕéšţöŕé ţĥîš ṽéŕšîöñ one two]';
@override
- String get paletteAllCommands => '[Åļļ çöɱɱåñðš one]';
+ String get historyShowChanges => '[Šĥöŵ çĥåñĝéš one]';
@override
- String get paletteCommands => '[Çöɱɱåñðš one]';
+ String get importFailed => '[Ţĥé îɱþöŕţ ƒåîļéð one tw]';
@override
- String get paletteEmpty => '[Ñöţĥîñĝ ƒöûñð one t]';
+ String get importUnsupported =>
+ '[Ţĥéšé ƒîļéš çåñ\'ţ ƀé îɱþöŕţéð ĥéŕé one two thr]';
@override
- String get paletteFiles => '[Ƒîļéš on]';
+ String get inviteInvalid => '[Ţĥîš ļîñķ îš ñöţ ṽåļîð one two]';
@override
- String get paletteGoToFile => '[Öþéñ ƒîļé one]';
+ String get inviteTitle => '[Öþéñîñĝ šĥåŕéð ƒîļé one tw]';
@override
- String get palettePlaceholder =>
- '[Ţýþé å çöɱɱåñð öŕ šéåŕçĥ ƒîļéš… one two th]';
+ String get kindDocument => '[Ðöçûɱéñţ one]';
@override
- String get paletteRecent => '[Ŕéçéñţ one]';
+ String get kindFile => '[Ƒîļé on]';
@override
- String get panelComments => '[Çöɱɱéñţš one]';
+ String get kindFolder => '[Ƒöļðéŕ one]';
@override
- String get panelHistory => '[Ṽéŕšîöñ ĥîšţöŕý one t]';
+ String get kindPresentation => '[Þŕéšéñţåţîöñ one t]';
@override
- String presenceJoined(String name) {
- return '[$name ĵöîñéð one]';
- }
+ String get kindSpreadsheet => '[Šþŕéåðšĥééţ one]';
@override
- String presenceLeft(String name) {
- return '[$name ļéƒţ on]';
- }
+ String get layoutGrid => '[Ĝŕîð ṽîéŵ one]';
@override
- String get quickSwitcherEmpty => '[Ñö ƒîļéš ƒöûñð one t]';
+ String get layoutList => '[Ļîšţ ṽîéŵ one]';
@override
- String get quickSwitcherPlaceholder =>
- '[Šéåŕçĥ ðöçûɱéñţš åñð šþŕéåðšĥééţš… one two thr]';
+ String get mcpAllowControl => '[Åļļöŵ ṀÇÞ ţö çöñţŕöļ ţĥîš ŵîñðöŵ one two th]';
@override
- String get quickSwitcherRecent => '[Ŕéçéñţ ƒîļéš one]';
+ String get mcpControlling =>
+ '[Åñ åĝéñţ îš çöñţŕöļļîñĝ ţĥîš ŵîñðöŵ one two thr]';
@override
- String get quickSwitcherResults => '[Ŕéšûļţš one]';
+ String get mcpHint =>
+ '[Ûšé ţĥîš çöñƒîĝûŕåţîöñ îñ Çļåûðé Ðéšķţöþ öŕ Çļåûðé Çöðé ŵîţĥ öñé öƒ ýöûŕ ţöķéñš. one two three four five]';
@override
- String get renameLabel => '[Ñåɱé on]';
+ String get mcpTitle => '[ṀÇÞ çöññéçţîöñ one t]';
@override
- String get renameTitle => '[Ŕéñåɱé one]';
+ String get menuAccessibility => '[Åççéššîƀîļîţý šéţţîñĝš one two]';
@override
- String get replyPlaceholder => '[Ŕéþļý öŕ åðð öţĥéŕš ŵîţĥ @ one two]';
+ String get menuAppTheme => '[Åþþ ţĥéɱé one]';
@override
- String get reportBugNote =>
- '[Κšûéš öñ ĜîţĤûƀ åŕé þûƀļîç: ðöñ\'ţ îñçļûðé þŕîṽåţé çöñţéñţ. one two three four]';
+ String get menuDownload => '[Ðöŵñļöåð one]';
@override
- String get requestAccessDescription =>
- '[Ţĥé öŵñéŕ ŵîļļ ƀé ñöţîƒîéð åñð çåñ šĥåŕé ţĥé ƒîļé ŵîţĥ ýöû. one two three fou]';
+ String get menuEdit => '[Éðîţ on]';
@override
- String get requestAccessFailed => '[Ţĥé ŕéǫûéšţ çöûļð ñöţ ƀé šéñţ one two t]';
+ String get menuExplore => '[Éẋþļöŕé one]';
@override
- String get requestAccessMessage => '[Ṁéššåĝé (öþţîöñåļ) one tw]';
+ String get menuFile => '[Ƒîļé on]';
@override
- String get requestAccessRole => '[Åççéšš one]';
+ String get menuFileUpload => '[Ƒîļé ûþļöåð one]';
@override
- String get requestAccessSend => '[Ŕéǫûéšţ åççéšš one t]';
+ String get menuHelp => '[Ĥéļþ on]';
@override
- String get requestAccessTitle => '[Ŕéǫûéšţ åççéšš one t]';
+ String get menuImportFile => '[Îɱþöŕţ ƒîļé… one]';
@override
- String get restoreVersionAction => '[Ŕéšţöŕé one]';
+ String get menuMakeCopy => '[Ṁåķé å çöþý one]';
@override
- String get restoreVersionBody =>
- '[Ţĥé çûŕŕéñţ ṽéŕšîöñ îš ķéþţ îñ ţĥé ĥîšţöŕý, šö ýöû çåñ šŵîţçĥ ƀåçķ åţ åñý ţîɱé. one two three four five]';
+ String get menuMode => '[Ṁöðé on]';
@override
- String get restoreVersionTitle => '[Ŕéšţöŕé ţĥîš ṽéŕšîöñ? one two]';
+ String get menuNew => '[Ñéŵ on]';
@override
- String get roleCommenter => '[Çöɱɱéñţéŕ one]';
+ String get menuNewDocument => '[Ðöçûɱéñţ one]';
@override
- String get roleEditor => '[Éðîţöŕ one]';
+ String get menuNewPresentation => '[Þŕéšéñţåţîöñ one t]';
@override
- String get roleOwner => '[Öŵñéŕ on]';
+ String get menuNewSpreadsheet => '[Šþŕéåðšĥééţ one]';
@override
- String get roleViewer => '[Ṽîéŵéŕ one]';
+ String get menuOpenApp => '[Öþéñ on]';
@override
- String get routeCreating => '[Çŕéåţîñĝ… one]';
+ String get menuPanels => '[Þåñéļš one]';
@override
- String get routeImportInvalid =>
- '[Ţĥîš ļîñķ ðöéšñ\'ţ ñåɱé å ƒîļé ţö îɱþöŕţ. one two thre]';
+ String get menuPreferences => '[Þŕéƒéŕéñçéš one]';
@override
- String get routeImporting => '[Îɱþöŕţîñĝ… one]';
+ String get menuShare => '[Šĥåŕé on]';
@override
- String searchAllDrive(String query) {
- return '[Šéåŕçĥ åļļ öƒ Ŵöñķý Ðŕîṽé ƒöŕ “$query” one two th]';
- }
+ String get menuTools => '[Ţööļš on]';
@override
- String searchFor(String query) {
- return '[Šéåŕçĥ ƒöŕ “$query” one]';
- }
+ String get menuVersionHistory => '[Ṽéŕšîöñ ĥîšţöŕý one t]';
@override
- String get searchHint => '[Šéåŕçĥ ƀý ñåɱé öŕ çöñţéñţ one two]';
+ String get menuView => '[Ṽîéŵ on]';
@override
- String get searchPlaceholder => '[Šéåŕçĥ îñ Ðŕîṽé one t]';
+ String get menuZoom => '[Žööɱ on]';
@override
- String searchResultsFor(String query) {
- return '[Ŕéšûļţš ƒöŕ “$query” one t]';
- }
+ String get menus => '[Ṁéñûš on]';
@override
- String selectedCount(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '$count šéļéçţéð',
- one: '1 šéļéçţéð',
- );
- return '[$_temp0 one tw]';
- }
+ String get moreActions => '[Ṁöŕé åçţîöñš one]';
@override
- String get sessionExpiredBody =>
- '[Šîĝñ îñ åĝåîñ ţö çöñţîñûé. Ýöûŕ ŵöŕķ îš ķéþţ. one two three]';
+ String get moveHere => '[Ṁöṽé ĥéŕé one]';
@override
- String get sessionExpiredTitle => '[Ýöûŕ šéššîöñ éẋþîŕéð one two]';
+ String get moveNoFolders => '[Ñö ƒöļðéŕš ĥéŕé one t]';
@override
- String get settingsAbout => '[Åƀöûţ on]';
+ String get moveNoPermission =>
+ '[Ýöû ðöñ\'ţ ĥåṽé þéŕɱîššîöñ ţö åðð îţéɱš ţö ţĥîš ƒöļðéŕ. one two three fo]';
@override
- String get settingsAccent => '[Åççéñţ çöļöŕ one]';
+ String moveTitleMany(int count) {
+ return '[Ṁöṽé $count îţéɱš one]';
+ }
@override
- String get settingsAccentNeutral => '[Ñéûţŕåļ one]';
+ String moveTitleOne(String name) {
+ return '[Ṁöṽé “$name” one]';
+ }
@override
- String get settingsAccount => '[Åççöûñţ one]';
+ String get nameVersionLabel => '[Ṽéŕšîöñ ñåɱé one]';
@override
- String get settingsAi => '[ÅÎ o]';
+ String get nameVersionTitle => '[Ñåɱé ţĥîš ṽéŕšîöñ one tw]';
@override
- String get settingsAiProviders => '[ÅÎ þŕöṽîðéŕš one]';
+ String get navApps => '[Åþþš on]';
@override
- String get settingsAiHint =>
- '[Ţĥé åššîšţåñţ ĥéļþš ýöû ŵŕîţé åñð åñåļýžé. Îţ öñļý šééš ƒîļéš ýöû öþéñ ŵîţĥ îţ. one two three four five]';
+ String get navHome => '[Ĥöɱé on]';
@override
- String get settingsAppearance => '[Åþþéåŕåñçé one]';
+ String get navMyDrive => '[Ṁý Ðŕîṽé one]';
@override
- String get settingsChangePassword => '[Çĥåñĝé þåššŵöŕð one t]';
+ String get navMyFiles => '[Ṁý ƒîļéš one]';
@override
- String get settingsDensity => '[Ðéñšîţý one]';
+ String get navOffline => '[Öƒƒļîñé one]';
@override
- String get settingsDeveloper => '[Ðéṽéļöþéŕ & ṀÇÞ one t]';
+ String get navRecent => '[Ŕéçéñţ one]';
@override
- String get settingsServerTitle => '[Šéŕṽéŕ one]';
+ String get navShared => '[Šĥåŕéð ŵîţĥ ɱé one t]';
@override
- String get settingsRedis => '[Ŕéðîš on]';
+ String get navStarred => '[Šţåŕŕéð one]';
@override
- String get settingsRedisHint =>
- '[Ṁéššåĝîñĝ ƀéţŵééñ šéŕṽéŕ îñšţåñçéš; ñééðéð ŵĥéñ ɱöŕé ţĥåñ öñé ŕûñš. one two three four fi]';
+ String get navStorage => '[Šţöŕåĝé one]';
@override
- String get redisConnected => '[Çöññéçţéð one]';
+ String get navTrash => '[Ţŕåšĥ on]';
@override
- String get redisNotReachable => '[Ñöţ ŕéåçĥåƀļé one t]';
+ String get newButton => '[Ñéŵ on]';
@override
- String get redisNotConfigured => '[Ñöţ çöñƒîĝûŕéð one t]';
+ String get newFolderLabel => '[Ƒöļðéŕ ñåɱé one]';
@override
- String get settingsKeyboard => '[Ķéýƀöåŕð šĥöŕţçûţš one tw]';
+ String get newFolderTitle => '[Ñéŵ ƒöļðéŕ one]';
@override
- String get settingsLanguage => '[Ļåñĝûåĝé & ŕéĝîöñ one tw]';
+ String noAccessBody(String email) {
+ return '[Ýöû ñééð åççéšš ţö öþéñ ţĥîš ƒîļé. Ýöû\'ŕé šîĝñéð îñ åš $email. one two three fo]';
+ }
@override
- String get settingsLanguageHint =>
- '[Ûšéð ƒöŕ ðåţéš, ñûɱƀéŕš åñð ñéŵ šþŕéåðšĥééţš. one two three]';
+ String get noAccessTitle => '[Ýöû ñééð åççéšš one t]';
@override
- String get settingsLayout => '[Ļåýöûţ one]';
+ String get notAllowed => '[Ñöţ åļļöŵéð one]';
@override
- String get settingsNotifications => '[Ñöţîƒîçåţîöñš one t]';
+ String get notFoundBody =>
+ '[Ţĥé þåĝé ýöû\'ŕé ļööķîñĝ ƒöŕ ðöéšñ\'ţ éẋîšţ. one two three]';
@override
- String get settingsOffline => '[Öƒƒļîñé & ðéṽîçé ðåţå one two]';
+ String get notFoundTitle => '[Þåĝé ñöţ ƒöûñð one t]';
@override
- String get settingsOfflineHint =>
- '[Ƒîļéš ýöû öþéñ ŕéçéñţļý šţåý åṽåîļåƀļé öñ ţĥîš ðéṽîçé. one two three fou]';
+ String notifAccessRequest(String who, String item) {
+ return '[$who åšķéð ƒöŕ åççéšš ţö $item one tw]';
+ }
@override
- String get settingsPasswordHint =>
- '[Ŵé\'ļļ éɱåîļ ýöû å çöðé ţö šéţ å ñéŵ þåššŵöŕð. one two three]';
+ String notifAssigned(String who, String item) {
+ return '[$who åššîĝñéð ýöû å çöɱɱéñţ îñ $item one two]';
+ }
@override
- String get settingsProfile => '[Þŕöƒîļé one]';
+ String notifMention(String who, String item) {
+ return '[$who ɱéñţîöñéð ýöû îñ $item one t]';
+ }
@override
- String get settingsReduceMotion => '[Ŕéðûçé ɱöţîöñ one t]';
+ String notifOwnership(String who, String item) {
+ return '[$who ŵåñţš ţö ɱåķé ýöû ţĥé öŵñéŕ öƒ $item one two t]';
+ }
@override
- String get settingsReduceMotionHint =>
- '[Ţûŕñš öƒƒ ñöñ-éššéñţîåļ åñîɱåţîöñš. one two thre]';
+ String notifReply(String who, String item) {
+ return '[$who ŕéþļîéð îñ $item one]';
+ }
@override
- String get settingsAccountProfile => '[Åççöûñţ & þŕöƒîļé one tw]';
+ String notifScriptFailure(String item) {
+ return '[Å šçŕîþţ ƒåîļéð îñ $item one tw]';
+ }
@override
- String settingsOpeningOtherApp(String settings) {
- return '[Öþéñîñĝ $settings… one]';
+ String notifShared(String who, String item) {
+ return '[$who šĥåŕéð $item ŵîţĥ ýöû one t]';
}
@override
- String get settingsOtherApps => '[Šéţţîñĝš öƒ öţĥéŕ åþþš one two]';
+ String get notifStorage =>
+ '[Ýöûŕ Ŵöñķý Ŵöŕķ šţöŕåĝé ñééðš åţţéñţîöñ one two thre]';
@override
- String get settingsSectionPicker => '[Šéţţîñĝš šéçţîöñ one tw]';
+ String notifBoardReply(String who, String title) {
+ return '[$who çöɱɱéñţéð öñ “$title” one t]';
+ }
@override
- String get settingsStorage => '[Šţöŕåĝé one]';
+ String notifBoardUpdate(String title) {
+ return '[Ûþðåţé öñ “$title” one]';
+ }
@override
- String get settingsStorageDocuments => '[Ðöçûɱéñţš one]';
+ String notifSuggestion(String who, String item) {
+ return '[$who šûĝĝéšţéð åñ éðîţ îñ $item one tw]';
+ }
@override
- String get settingsStorageFiles => '[Ƒîļéš on]';
+ String notifSuggestionDecided(String who, String item) {
+ return '[$who ŕéṽîéŵéð ýöûŕ šûĝĝéšţîöñ îñ $item one two t]';
+ }
@override
- String get settingsStorageLoading => '[Ļöåðîñĝ šţöŕåĝé… one tw]';
+ String get notificationsEmpty => '[Ýöû\'ŕé åļļ çåûĝĥţ ûþ one tw]';
@override
- String get settingsStorageManage =>
- '[Ṁåñåĝé šţöŕåĝé îñ Ŵöñķý Ðŕîṽé one two t]';
+ String get notificationsMarkAllRead => '[Ṁåŕķ åļļ åš ŕéåð one t]';
@override
- String get settingsStoragePresentations => '[Þŕéšéñţåţîöñš one t]';
+ String get notificationsTitle => '[Ñöţîƒîçåţîöñš one t]';
@override
- String get settingsStorageQuota => '[Ýöûŕ šţöŕåĝé one]';
+ String notificationsUnread(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count ûñŕéåð ñöţîƒîçåţîöñš',
+ one: '1 ûñŕéåð ñöţîƒîçåţîöñ',
+ );
+ return '[$_temp0 one two three]';
+ }
@override
- String get settingsStorageQuotaHint =>
- '[Öñé ǫûöţå ƒöŕ åļļ Ŵöñķý Ŵöŕķ åþþš. one two th]';
+ String get notifyEmail => '[Îñ-åþþ åñð éɱåîļ one t]';
@override
- String get settingsStorageSpreadsheets => '[Šþŕéåðšĥééţš one t]';
+ String get notifyInApp => '[Îñ-åþþ one]';
@override
- String get settingsStorageTrash => '[Ţŕåšĥ on]';
+ String get notifyMentions => '[Ṁéñţîöñš one]';
@override
- String get settingsStorageUnavailable =>
- '[Šţöŕåĝé ûšåĝé îšñ\'ţ åṽåîļåƀļé ŕîĝĥţ ñöŵ. one two three]';
+ String get notifyOff => '[Öƒƒ on]';
@override
- String get settingsStorageWhatCounts => '[Ŵĥåţ çöûñţš one]';
+ String get notifyReplies => '[Ŕéþļîéš ţö ɱý çöɱɱéñţš one two]';
@override
- String get settingsStorageWhatCountsBody =>
- '[Ðöçûɱéñţš, šþŕéåðšĥééţš, þŕéšéñţåţîöñš, ûþļöåðéð ƒîļéš åñð îɱåĝéš îñ ýöûŕ ƒîļéš, îñçļûðîñĝ ţĥé ţŕåšĥ. Ṽéŕšîöñ ĥîšţöŕý, ţĥûɱƀñåîļš åñð éẋþöŕţš ðöñ\'ţ çöûñţ. Ƒîļéš šĥåŕéð ŵîţĥ ýöû çöûñţ åĝåîñšţ ţĥéîŕ öŵñéŕ\'š šţöŕåĝé. one two three four five six seven eight nine ten one two three fo]';
+ String get notifyShares => '[Ƒîļéš šĥåŕéð ŵîţĥ ɱé one tw]';
@override
- String get shortcutsGroupShared => '[Šĥåŕéð one]';
+ String get offlineBanner =>
+ '[Ýöû\'ŕé öƒƒļîñé. Çĥåñĝéš åŕé šåṽéð öñ ţĥîš ðéṽîçé åñð ŵîļļ šýñç ŵĥéñ ýöû\'ŕé ƀåçķ öñļîñé. one two three four five si]';
@override
- String get settingsSaved => '[Šåṽéð on]';
+ String get offlineClear => '[Çļéåŕ ļöçåļ ðåţå one t]';
@override
- String get settingsSecurity => '[Šîĝñ-îñ & šéçûŕîţý one tw]';
+ String get offlineClearHint =>
+ '[Ŕéɱöṽéš çåçĥéð ƒîļéš åñð þéñðîñĝ çĥåñĝéš ƒŕöɱ ţĥîš ðéṽîçé. one two three four]';
@override
- String get settingsSheets => '[Ŵöñķý Šĥééţš one]';
+ String get offlineCleared => '[Ļöçåļ ðåţå çļéåŕéð one tw]';
@override
- String get settingsSignOutHint =>
- '[Šîĝñš ýöû öûţ öƒ Ŵöñķý Ŵöŕķ öñ ţĥîš ðéṽîçé. one two three]';
+ String get offlinePending => '[Çĥåñĝéš ŵåîţîñĝ ţö šýñç one two]';
@override
- String get settingsTheme => '[Ţĥéɱé on]';
+ String get badgeOfflineRecent =>
+ '[Åṽåîļåƀļé öƒƒļîñé (öþéñéð ŕéçéñţļý) one two thre]';
@override
- String get settingsTimeZone => '[Ţîɱé žöñé one]';
+ String get badgeOfflinePending => '[Ðöŵñļöåðîñĝ ƒöŕ öƒƒļîñé ûšé… one two t]';
@override
- String get settingsTimeZoneHint =>
- '[ÎÅÑÅ ñåɱé, é.ĝ. Éûŕöþé/Šţöçķĥöļɱ. Éɱþţý ƒöļļöŵš ţĥîš ðéṽîçé. one two three four]';
+ String get offlineUnavailable =>
+ '[Ţĥîš ƒîļé îšñ\'ţ åṽåîļåƀļé öƒƒļîñé one two thr]';
@override
- String get settingsUiLanguage => '[Ļåñĝûåĝé one]';
+ String get offlineDocuments => '[Ƒîļéš öñ ţĥîš ðéṽîçé one tw]';
@override
- String get settingsWrite => '[Ŵöñķý Ŵŕîţé one]';
+ String offlineDocumentsValue(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count ƒîļéš',
+ one: '1 ƒîļé',
+ zero: 'Ñöñé',
+ );
+ return '[$_temp0 one t]';
+ }
@override
- String get shareAddPeople => '[Åðð þéöþļé ƀý ñåɱé öŕ éɱåîļ one two]';
+ String offlinePinnedCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count ɱåðé åṽåîļåƀļé öƒƒļîñé',
+ one: '1 ɱåðé åṽåîļåƀļé öƒƒļîñé',
+ zero: 'Ñö ƒîļéš ɱåðé åṽåîļåƀļé öƒƒļîñé',
+ );
+ return '[$_temp0 one two three four five]';
+ }
@override
- String get shareAnyoneWithLink => '[Åñýöñé ŵîţĥ ţĥé ļîñķ one tw]';
+ String get offlineUploads => '[Îɱåĝéš ŵåîţîñĝ ţö ûþļöåð one two]';
@override
- String get shareApprove => '[Åþþŕöṽé one]';
+ String get offlineStorageUsed => '[Šţöŕåĝé ûšéð one]';
@override
- String get shareButton => '[Šĥåŕé on]';
+ String offlineClearUnsynced(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count çĥåñĝéš ĥåṽéñ\'ţ šýñçéð ýéţ åñð ŵîļļ ƀé ļöšţ.',
+ one: '1 çĥåñĝé ĥåšñ\'ţ šýñçéð ýéţ åñð ŵîļļ ƀé ļöšţ.',
+ );
+ return '[$_temp0 one two three four five si]';
+ }
@override
- String get shareCannotShare =>
- '[Öñļý ţĥé öŵñéŕ åñð éðîţöŕš çåñ šĥåŕé ţĥîš ƒîļé. one two three]';
+ String get offlineSimulate => '[Šîɱûļåţé öƒƒļîñé one tw]';
@override
- String get shareDeny => '[Ðéñý on]';
+ String get offlineSimulateHint =>
+ '[Ðîšçöññéçţš ƒŕöɱ ţĥé šéŕṽéŕ ţö ţŕý öƒƒļîñé éðîţîñĝ (ðéṽéļöþéŕ šéţţîñĝ). one two three four fiv]';
@override
- String get shareFailed =>
- '[Ţĥé šĥåŕîñĝ çĥåñĝé çöûļð ñöţ ƀé šåṽéð one two thr]';
+ String get ownerAnyone => '[Öŵñéð ƀý åñýöñé one t]';
@override
- String get shareGeneralAccess => '[Ĝéñéŕåļ åççéšš one t]';
+ String get ownerMe => '[ɱé o]';
@override
- String shareInherited(String folder) {
- return '[Ƒŕöɱ ƒöļðéŕ “$folder” one t]';
- }
+ String get ownerMeFilter => '[Öŵñéð ƀý ɱé one]';
@override
- String get shareLinkHint =>
- '[Åñýöñé šîĝñéð îñ ŵĥö ĥåš ţĥé ļîñķ çåñ öþéñ îţ one two three]';
+ String get ownerNotMe => '[Ñöţ öŵñéð ƀý ɱé one t]';
@override
- String get shareMessage => '[Ṁéššåĝé one]';
+ String get paletteAllCommands => '[Åļļ çöɱɱåñðš one]';
@override
- String get shareNotify => '[Ñöţîƒý þéöþļé one t]';
+ String get paletteCommands => '[Çöɱɱåñðš one]';
@override
- String get sharePending => '[Þéñðîñĝ îñṽîţé one t]';
+ String get paletteEmpty => '[Ñöţĥîñĝ ƒöûñð one t]';
@override
- String get sharePeopleWithAccess => '[Þéöþļé ŵîţĥ åççéšš one tw]';
+ String get paletteFiles => '[Ƒîļéš on]';
@override
- String get shareRemoveAccess => '[Ŕéɱöṽé åççéšš one t]';
+ String get paletteGoToFile => '[Öþéñ ƒîļé one]';
@override
- String shareRemoveBody(String name) {
- return '[$name ŵîļļ ļöšé åççéšš ţö ţĥîš ƒîļé. one two t]';
- }
+ String get palettePlaceholder =>
+ '[Ţýþé å çöɱɱåñð öŕ šéåŕçĥ ƒîļéš… one two th]';
@override
- String shareRemoveRecipient(String email) {
- return '[Ŕéɱöṽé $email one]';
- }
+ String get paletteRecent => '[Ŕéçéñţ one]';
@override
- String get shareRemoveTitle => '[Ŕéɱöṽé åççéšš? one t]';
+ String get panelComments => '[Çöɱɱéñţš one]';
@override
- String shareRequestTitle(String name, String role) {
- return '[$name ŕéǫûéšţš $role åççéšš one t]';
- }
+ String get panelHistory => '[Ṽéŕšîöñ ĥîšţöŕý one t]';
@override
- String get shareRestricted => '[Ŕéšţŕîçţéð one]';
+ String presenceJoined(String name) {
+ return '[$name ĵöîñéð one]';
+ }
@override
- String get shareRestrictedHint =>
- '[Öñļý þéöþļé ŵîţĥ åççéšš çåñ öþéñ ŵîţĥ ţĥé ļîñķ one two three]';
+ String presenceLeft(String name) {
+ return '[$name ļéƒţ on]';
+ }
@override
- String get shareRotateLink => '[Ŕéšéţ ļîñķ one]';
+ String get quickSwitcherEmpty => '[Ñö ƒîļéš ƒöûñð one t]';
@override
- String shareSent(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: 'Šĥåŕéð ŵîţĥ $count þéöþļé',
- one: 'Šĥåŕéð ŵîţĥ 1 þéŕšöñ',
- );
- return '[$_temp0 one two thre]';
- }
+ String get quickSwitcherPlaceholder =>
+ '[Šéåŕçĥ ðöçûɱéñţš åñð šþŕéåðšĥééţš… one two thr]';
@override
- String get shareSettingComments => '[Ṽîéŵéŕš çåñ šéé çöɱɱéñţš one two]';
+ String get quickSwitcherRecent => '[Ŕéçéñţ ƒîļéš one]';
@override
- String get shareSettingEditors =>
- '[Éðîţöŕš çåñ çĥåñĝé þéŕɱîššîöñš åñð šĥåŕé one two three]';
+ String get quickSwitcherResults => '[Ŕéšûļţš one]';
@override
- String get shareSettingExport =>
- '[Ṽîéŵéŕš åñð çöɱɱéñţéŕš çåñ ðöŵñļöåð, þŕîñţ åñð çöþý one two three fo]';
+ String get renameLabel => '[Ñåɱé on]';
@override
- String get shareSettingsTitle => '[Šĥåŕîñĝ šéţţîñĝš one tw]';
+ String get renameTitle => '[Ŕéñåɱé one]';
@override
- String shareTitle(String name) {
- return '[Šĥåŕé “$name” one]';
- }
+ String get replyPlaceholder => '[Ŕéþļý öŕ åðð öţĥéŕš ŵîţĥ @ one two]';
@override
- String shareTransferBody(String name) {
- return '[$name ƀéçöɱéš ţĥé öŵñéŕ öñçé ţĥéý åççéþţ. Ýöû\'ļļ šţåý åñ éðîţöŕ. one two three four]';
- }
+ String get reportBugNote =>
+ '[Κšûéš öñ ĜîţĤûƀ åŕé þûƀļîç: ðöñ\'ţ îñçļûðé þŕîṽåţé çöñţéñţ. one two three four]';
@override
- String get shareTransferOwnership => '[Ţŕåñšƒéŕ öŵñéŕšĥîþ one tw]';
+ String get requestAccessDescription =>
+ '[Ţĥé öŵñéŕ ŵîļļ ƀé ñöţîƒîéð åñð çåñ šĥåŕé ţĥé ƒîļé ŵîţĥ ýöû. one two three fou]';
@override
- String shareTransferSent(String name) {
- return '[Öŵñéŕšĥîþ ţŕåñšƒéŕ šéñţ ţö $name one two t]';
- }
+ String get requestAccessFailed => '[Ţĥé ŕéǫûéšţ çöûļð ñöţ ƀé šéñţ one two t]';
@override
- String get shareTransferTitle => '[Ţŕåñšƒéŕ öŵñéŕšĥîþ? one two]';
+ String get requestAccessMessage => '[Ṁéššåĝé (öþţîöñåļ) one tw]';
@override
- String shareYou(String name) {
- return '[$name (ýöû) on]';
- }
+ String get requestAccessRole => '[Åççéšš one]';
@override
- String get sheetsAutocomplete => '[Ƒöŕɱûļå šûĝĝéšţîöñš one two]';
+ String get requestAccessSend => '[Ŕéǫûéšţ åççéšš one t]';
@override
- String get sheetsFormulaHelp => '[Šĥöŵ ƒöŕɱûļå ĥéļþ one tw]';
+ String get requestAccessTitle => '[Ŕéǫûéšţ åççéšš one t]';
@override
- String get sheetsScripts => '[Åļļöŵ šçŕîþţš one t]';
+ String get restoreVersionAction => '[Ŕéšţöŕé one]';
@override
- String shortcutConflictBody(String keys, String commands) {
- return '[$keys îš åļŕéåðý ûšéð ƀý $commands. Ŕéþļåçé îţ? one two th]';
- }
+ String get restoreVersionBody =>
+ '[Ţĥé çûŕŕéñţ ṽéŕšîöñ îš ķéþţ îñ ţĥé ĥîšţöŕý, šö ýöû çåñ šŵîţçĥ ƀåçķ åţ åñý ţîɱé. one two three four five]';
@override
- String get shortcutConflictTitle => '[Šĥöŕţçûţ îñ ûšé one t]';
+ String get restoreVersionTitle => '[Ŕéšţöŕé ţĥîš ṽéŕšîöñ? one two]';
@override
- String get shortcutReplace => '[Ŕéþļåçé one]';
+ String get roleCommenter => '[Çöɱɱéñţéŕ one]';
@override
- String get shortcutReserved =>
- '[Ţĥé ƀŕöŵšéŕ ŕéšéŕṽéš ţĥîš šĥöŕţçûţ. one two thr]';
+ String get roleEditor => '[Éðîţöŕ one]';
@override
- String get shortcutReset => '[Ŕéšéţ ţö ðéƒåûļţ one t]';
+ String get roleOwner => '[Öŵñéŕ on]';
@override
- String get shortcutResetAll => '[Ŕéšéţ åļļ one]';
+ String get roleViewer => '[Ṽîéŵéŕ one]';
@override
- String get shortcutsSearch => '[Šéåŕçĥ šĥöŕţçûţš one tw]';
+ String get routeCreating => '[Çŕéåţîñĝ… one]';
@override
- String get shortcutsTitle => '[Ķéýƀöåŕð šĥöŕţçûţš one tw]';
+ String get routeImportInvalid =>
+ '[Ţĥîš ļîñķ ðöéšñ\'ţ ñåɱé å ƒîļé ţö îɱþöŕţ. one two thre]';
@override
- String get someone => '[Šöɱéöñé one]';
+ String get routeImporting => '[Îɱþöŕţîñĝ… one]';
@override
- String sortBy(String field) {
- return '[Šöŕţ: $field on]';
+ String searchAllDrive(String query) {
+ return '[Šéåŕçĥ åļļ öƒ Ŵöñķý Ðŕîṽé ƒöŕ “$query” one two th]';
}
@override
- String get sortCreated => '[Çŕéåţéð one]';
+ String searchFor(String query) {
+ return '[Šéåŕçĥ ƒöŕ “$query” one]';
+ }
@override
- String get sortDescending => '[Ðéšçéñðîñĝ one]';
+ String get searchHint => '[Šéåŕçĥ ƀý ñåɱé öŕ çöñţéñţ one two]';
@override
- String get sortModified => '[Ļåšţ ɱöðîƒîéð one t]';
+ String get searchPlaceholder => '[Šéåŕçĥ îñ Ðŕîṽé one t]';
@override
- String get sortModifiedByMe => '[Ļåšţ ɱöðîƒîéð ƀý ɱé one tw]';
+ String searchResultsFor(String query) {
+ return '[Ŕéšûļţš ƒöŕ “$query” one t]';
+ }
@override
- String get sortName => '[Ñåɱé on]';
-
- @override
- String get sortOpenedByMe => '[Ļåšţ öþéñéð ƀý ɱé one t]';
+ String selectedCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count šéļéçţéð',
+ one: '1 šéļéçţéð',
+ );
+ return '[$_temp0 one tw]';
+ }
@override
- String get sortSize => '[Ƒîļé šîžé one]';
+ String get sessionExpiredBody =>
+ '[Šîĝñ îñ åĝåîñ ţö çöñţîñûé. Ýöûŕ ŵöŕķ îš ķéþţ. one two three]';
@override
- String storageMeterValue(String usage, int percent) {
- return '[$usage, $percent þéŕçéñţ one]';
- }
+ String get sessionExpiredTitle => '[Ýöûŕ šéššîöñ éẋþîŕéð one two]';
@override
- String storageUsed(String size) {
- return '[$size ûšéð on]';
- }
+ String get settingsAbout => '[Åƀöûţ on]';
@override
- String storageUsedOf(String used, String quota) {
- return '[$used öƒ $quota ûšéð one]';
- }
+ String get settingsAccent => '[Åççéñţ çöļöŕ one]';
@override
- String storageUsedOfOver(String used, String quota) {
- return '[$used öƒ $quota ûšéð — öṽéŕ ǫûöţå one tw]';
- }
+ String get settingsAccentNeutral => '[Ñéûţŕåļ one]';
@override
- String get storageCalculating => '[Çåļçûļåţîñĝ šţöŕåĝé… one two]';
+ String get settingsAccount => '[Åççöûñţ one]';
@override
- String get storageMeterTooltip =>
- '[Šţöŕåĝé îš šĥåŕéð ƀý Ŵöñķý Ðŕîṽé, Ŵŕîţé, Šĥééţš åñð Šļîðéš. one two three four]';
+ String get settingsAi => '[ÅÎ o]';
@override
- String get storageMeterTooltipOffline =>
- '[Šţöŕåĝé îš šĥåŕéð ƀý Ŵöñķý Ðŕîṽé, Ŵŕîţé, Šĥééţš åñð Šļîðéš. (öƒƒļîñé) one two three four fi]';
+ String get settingsAiProviders => '[ÅÎ þŕöṽîðéŕš one]';
@override
- String storageBannerWarning(String used, String quota) {
- return '[Ýöû\'ṽé ûšéð $used öƒ ýöûŕ $quota šţöŕåĝé. one two t]';
- }
+ String get settingsAiHint =>
+ '[Ţĥé åššîšţåñţ ĥéļþš ýöû ŵŕîţé åñð åñåļýžé. Îţ öñļý šééš ƒîļéš ýöû öþéñ ŵîţĥ îţ. one two three four five]';
@override
- String get storageBannerOver =>
- '[Ýöû\'ŕé öûţ öƒ šţöŕåĝé. Ýöû çåñ ķééþ éðîţîñĝ, ƀûţ ýöû çåñ\'ţ ûþļöåð ƒîļéš, åðð îɱåĝéš, îɱþöŕţ, öŕ ɱåķé çöþîéš ûñţîļ ýöû ƒŕéé ûþ šþåçé. one two three four five six seven eight]';
+ String get settingsAppearance => '[Åþþéåŕåñçé one]';
@override
- String get storageBannerDismiss => '[Ðîšɱîšš one]';
+ String get settingsChangePassword => '[Çĥåñĝé þåššŵöŕð one t]';
@override
- String get quotaOwnTitle => '[Ýöû\'ŕé öûţ öƒ šţöŕåĝé one two]';
+ String get settingsDensity => '[Ðéñšîţý one]';
@override
- String quotaOwnBody(String needed, String available, String quota) {
- return '[Ţĥîš ñééðš $needed, ƀûţ ýöû ĥåṽé $available ļéƒţ öƒ ýöûŕ $quota. Ƒŕéé ûþ šþåçé îñ Ŵöñķý Ðŕîṽé ƀý ðéļéţîñĝ ƒîļéš ýöû ñö ļöñĝéŕ ñééð åñð éɱþţýîñĝ ţĥé ţŕåšĥ. one two three four five six seven eig]';
- }
+ String get settingsDeveloper => '[Ðéṽéļöþéŕ & ṀÇÞ one t]';
@override
- String quotaOwnBodyUnknown(String available, String quota) {
- return '[Ýöû ĥåṽé $available ļéƒţ öƒ ýöûŕ $quota. Ƒŕéé ûþ šþåçé îñ Ŵöñķý Ðŕîṽé ƀý ðéļéţîñĝ ƒîļéš ýöû ñö ļöñĝéŕ ñééð åñð éɱþţýîñĝ ţĥé ţŕåšĥ. one two three four five six seve]';
- }
+ String get settingsServerTitle => '[Šéŕṽéŕ one]';
@override
- String quotaOwnerTitle(String owner) {
- return '[$owner îš öûţ öƒ šţöŕåĝé one t]';
- }
+ String get settingsRedis => '[Ŕéðîš on]';
@override
- String quotaOwnerImageBody(String kind, String owner) {
- String _temp0 = intl.Intl.selectLogic(kind, {
- 'spreadsheet':
- 'Îɱåĝéš åððéð ţö ţĥîš šþŕéåðšĥééţ çöûñţ åĝåîñšţ ţĥé öŵñéŕ\'š šţöŕåĝé. Åšķ $owner ţö ƒŕéé ûþ šþåçé, öŕ åðð ţĥé îɱåĝé ţö å šþŕéåðšĥééţ ýöû öŵñ.',
- 'presentation':
- 'Îɱåĝéš åððéð ţö ţĥîš þŕéšéñţåţîöñ çöûñţ åĝåîñšţ ţĥé öŵñéŕ\'š šţöŕåĝé. Åšķ $owner ţö ƒŕéé ûþ šþåçé, öŕ åðð ţĥé îɱåĝé ţö å þŕéšéñţåţîöñ ýöû öŵñ.',
- 'other':
- 'Îɱåĝéš åððéð ţö ţĥîš ðöçûɱéñţ çöûñţ åĝåîñšţ ţĥé öŵñéŕ\'š šţöŕåĝé. Åšķ $owner ţö ƒŕéé ûþ šþåçé, öŕ åðð ţĥé îɱåĝé ţö å ðöçûɱéñţ ýöû öŵñ.',
- });
- return '[$_temp0 one two three four five six seven eight nine ten one two three four five six seven eight nine ten one two three fo]';
- }
+ String get settingsRedisHint =>
+ '[Ṁéššåĝîñĝ ƀéţŵééñ šéŕṽéŕ îñšţåñçéš; ñééðéð ŵĥéñ ɱöŕé ţĥåñ öñé ŕûñš. one two three four fi]';
@override
- String quotaOwnerBody(String owner) {
- return '[Çöñţéñţ åððéð ţö ţĥîš ƒîļé çöûñţš åĝåîñšţ ţĥé öŵñéŕ\'š šţöŕåĝé. Åšķ $owner ţö ƒŕéé ûþ šþåçé ƒîŕšţ. one two three four five six]';
- }
+ String get redisConnected => '[Çöññéçţéð one]';
@override
- String get quotaBatchTitle => '[Ñöţ éñöûĝĥ šţöŕåĝé ƒöŕ åļļ ƒîļéš one two th]';
+ String get redisNotReachable => '[Ñöţ ŕéåçĥåƀļé one t]';
@override
- String quotaBatchBody(int count, String needed, String available) {
- return '[Ţĥéšé $count ƒîļéš ñééð $needed, ƀûţ ýöû ĥåṽé $available ļéƒţ. Ûþļöåð ţĥé ƒîļéš ţĥåţ ƒîţ, öŕ ƒŕéé ûþ šþåçé ƒîŕšţ. one two three four five s]';
- }
+ String get redisNotConfigured => '[Ñöţ çöñƒîĝûŕéð one t]';
@override
- String get quotaBatchFit => '[Ûþļöåð ţĥé ƒîļéš ţĥåţ ƒîţ one two]';
+ String get settingsKeyboard => '[Ķéýƀöåŕð šĥöŕţçûţš one tw]';
@override
- String quotaTransferTitle(String name) {
- return '[$name ðöéšñ\'ţ ĥåṽé éñöûĝĥ šţöŕåĝé one two t]';
- }
+ String get settingsLanguage => '[Ļåñĝûåĝé & ŕéĝîöñ one tw]';
@override
- String quotaTransferBody(String name, String needed, String item) {
- return '[$name ñééðš $needed ƒŕéé ţö ţåķé öŵñéŕšĥîþ öƒ “$item”. Åšķ ţĥéɱ ţö ƒŕéé ûþ šþåçé ƒîŕšţ. one two three four f]';
- }
+ String get settingsLanguageHint =>
+ '[Ûšéð ƒöŕ ðåţéš, ñûɱƀéŕš åñð ñéŵ šþŕéåðšĥééţš. one two three]';
@override
- String get quotaTransferAcceptTitle =>
- '[Ýöû ðöñ\'ţ ĥåṽé éñöûĝĥ šţöŕåĝé one two t]';
+ String get settingsLayout => '[Ļåýöûţ one]';
@override
- String quotaTransferAcceptBody(String item, String needed, String available) {
- return '[Ţåķîñĝ öŵñéŕšĥîþ öƒ “$item” ñééðš $needed, ƀûţ ýöû ĥåṽé $available ļéƒţ. one two three]';
- }
+ String get settingsNotifications => '[Ñöţîƒîçåţîöñš one t]';
@override
- String get quotaTooLargeTitle => '[Ţĥîš ƒîļé îš ţöö ļåŕĝé one two]';
+ String get settingsOffline => '[Öƒƒļîñé & ðéṽîçé ðåţå one two]';
@override
- String quotaTooLargeBody(String max) {
- return '[Ƒîļéš çåñ ƀé åţ ɱöšţ $max. one tw]';
- }
+ String get settingsOfflineHint =>
+ '[Ƒîļéš ýöû öþéñ ŕéçéñţļý šţåý åṽåîļåƀļé öñ ţĥîš ðéṽîçé. one two three fou]';
@override
- String quotaTooLargeNowBody(String max) {
- return '[Ƒîļéš çåñ ƀé åţ ɱöšţ $max ŕîĝĥţ ñöŵ, ƀéçåûšé ţĥåţ\'š ĥöŵ ɱûçĥ šţöŕåĝé ýöû ĥåṽé ļéƒţ. one two three four five]';
- }
+ String get settingsPasswordHint =>
+ '[Ŵé\'ļļ éɱåîļ ýöû å çöðé ţö šéţ å ñéŵ þåššŵöŕð. one two three]';
@override
- String get quotaItemFallback => '[ţĥîš îţéɱ one]';
+ String get settingsProfile => '[Þŕöƒîļé one]';
@override
- String get strengthEmpty => '[Éñţéŕ å þåššŵöŕð one t]';
+ String get settingsReduceMotion => '[Ŕéðûçé ɱöţîöñ one t]';
@override
- String get strengthFair => '[Ƒåîŕ on]';
+ String get settingsReduceMotionHint =>
+ '[Ţûŕñš öƒƒ ñöñ-éššéñţîåļ åñîɱåţîöñš. one two thre]';
@override
- String get strengthGood => '[Ĝööð on]';
+ String get settingsAccountProfile => '[Åççöûñţ & þŕöƒîļé one tw]';
@override
- String strengthLabel(String label) {
- return '[Þåššŵöŕð šţŕéñĝţĥ: $label one tw]';
+ String settingsOpeningOtherApp(String settings) {
+ return '[Öþéñîñĝ $settings… one]';
}
@override
- String get strengthStrong => '[Šţŕöñĝ one]';
+ String get settingsOtherApps => '[Šéţţîñĝš öƒ öţĥéŕ åþþš one two]';
@override
- String get strengthWeak => '[Ŵéåķ on]';
+ String get settingsSectionPicker => '[Šéţţîñĝš šéçţîöñ one tw]';
@override
- String get switchAccount => '[Šŵîţçĥ åççöûñţ one t]';
+ String get settingsStorage => '[Šţöŕåĝé one]';
@override
- String get templateBlank => '[Ɓļåñķ on]';
+ String get settingsStorageDocuments => '[Ðöçûɱéñţš one]';
@override
- String get templateEducation => '[Éðûçåţîöñ one]';
+ String get settingsStorageFiles => '[Ƒîļéš on]';
@override
- String get templateFinance => '[Ƒîñåñçé one]';
+ String get settingsStorageLoading => '[Ļöåðîñĝ šţöŕåĝé… one tw]';
@override
- String get templateGallery => '[Ţéɱþļåţé ĝåļļéŕý one tw]';
+ String get settingsStorageManage =>
+ '[Ṁåñåĝé šţöŕåĝé îñ Ŵöñķý Ðŕîṽé one two t]';
@override
- String get templatePersonal => '[Þéŕšöñåļ one]';
+ String get settingsStoragePresentations => '[Þŕéšéñţåţîöñš one t]';
@override
- String get templateProjectManagement => '[Þŕöĵéçţ ɱåñåĝéɱéñţ one tw]';
+ String get settingsStorageQuota => '[Ýöûŕ šţöŕåĝé one]';
@override
- String templateStripTitle(String kind) {
- return '[Šţåŕţ å ñéŵ $kind one]';
- }
+ String get settingsStorageQuotaHint =>
+ '[Öñé ǫûöţå ƒöŕ åļļ Ŵöñķý Ŵöŕķ åþþš. one two th]';
@override
- String get templateWork => '[Ŵöŕķ on]';
+ String get settingsStorageSpreadsheets => '[Šþŕéåðšĥééţš one t]';
@override
- String get templatesHide => '[Ĥîðé ţéɱþļåţéš one t]';
+ String get settingsStorageTrash => '[Ţŕåšĥ on]';
@override
- String get templatesShow => '[Šĥöŵ ţéɱþļåţéš one t]';
+ String get settingsStorageUnavailable =>
+ '[Šţöŕåĝé ûšåĝé îšñ\'ţ åṽåîļåƀļé ŕîĝĥţ ñöŵ. one two three]';
@override
- String get themeDark => '[Ðåŕķ on]';
+ String get settingsStorageWhatCounts => '[Ŵĥåţ çöûñţš one]';
@override
- String get themeLight => '[Ļîĝĥţ on]';
+ String get settingsStorageWhatCountsBody =>
+ '[Ðöçûɱéñţš, šþŕéåðšĥééţš, þŕéšéñţåţîöñš, ûþļöåðéð ƒîļéš åñð îɱåĝéš îñ ýöûŕ ƒîļéš, îñçļûðîñĝ ţĥé ţŕåšĥ. Ṽéŕšîöñ ĥîšţöŕý, ţĥûɱƀñåîļš åñð éẋþöŕţš ðöñ\'ţ çöûñţ. Ƒîļéš šĥåŕéð ŵîţĥ ýöû çöûñţ åĝåîñšţ ţĥéîŕ öŵñéŕ\'š šţöŕåĝé. one two three four five six seven eight nine ten one two three fo]';
@override
- String get themeSystem => '[Šýšţéɱ one]';
+ String get shortcutsGroupShared => '[Šĥåŕéð one]';
@override
- String themeToggleTooltip(String mode) {
- return '[Ţĥéɱé: $mode one]';
- }
+ String get settingsSaved => '[Šåṽéð on]';
@override
- String timeHoursAgo(int hours) {
- return '[$hours ĥ åĝö on]';
- }
+ String get settingsSecurity => '[Šîĝñ-îñ & šéçûŕîţý one tw]';
@override
- String get timeJustNow => '[Ĵûšţ ñöŵ one]';
+ String get settingsSheets => '[Ŵöñķý Šĥééţš one]';
@override
- String timeMinutesAgo(int minutes) {
- return '[$minutes ɱîñ åĝö one]';
- }
+ String get settingsSignOutHint =>
+ '[Šîĝñš ýöû öûţ öƒ Ŵöñķý Ŵöŕķ öñ ţĥîš ðéṽîçé. one two three]';
@override
- String get toastAccessRequested => '[Ŕéǫûéšţ šéñţ one]';
+ String get settingsTheme => '[Ţĥéɱé on]';
@override
- String toastCopyCreated(String name) {
- return '[Çŕéåţéð “$name” one]';
- }
+ String get settingsTimeZone => '[Ţîɱé žöñé one]';
@override
- String get toastLinkCopied => '[Ļîñķ çöþîéð one]';
+ String get settingsTimeZoneHint =>
+ '[ÎÅÑÅ ñåɱé, é.ĝ. Éûŕöþé/Šţöçķĥöļɱ. Éɱþţý ƒöļļöŵš ţĥîš ðéṽîçé. one two three four]';
@override
- String get toastMcpConfigCopied => '[ṀÇÞ šéţţîñĝš çöþîéð one tw]';
+ String get settingsUiLanguage => '[Ļåñĝûåĝé one]';
@override
- String toastMovedToTrash(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '$count îţéɱš ɱöṽéð ţö ţŕåšĥ',
- one: 'Ṁöṽéð ţö ţŕåšĥ',
- );
- return '[$_temp0 one two thr]';
- }
+ String get settingsWrite => '[Ŵöñķý Ŵŕîţé one]';
@override
- String get toastOfflineOpFailed =>
- '[Å çĥåñĝé ɱåðé öƒƒļîñé çöûļð ñöţ ƀé åþþļîéð one two three]';
+ String get shareAddPeople => '[Åðð þéöþļé ƀý ñåɱé öŕ éɱåîļ one two]';
@override
- String get toastShortcutAdded => '[Šĥöŕţçûţ åððéð one t]';
+ String get shareAnyoneWithLink => '[Åñýöñé ŵîţĥ ţĥé ļîñķ one tw]';
@override
- String get toastUndoPartial =>
- '[Šöɱé çĥåñĝéš çöûļðñ\'ţ ƀé ûñðöñé ƀéçåûšé ţĥéý ŵéŕé éðîţéð ƀý šöɱéöñé éļšé. one two three four fiv]';
+ String get shareApprove => '[Åþþŕöṽé one]';
@override
- String toastVersionNamed(String name) {
- return '[Šåṽéð ṽéŕšîöñ “$name” one t]';
- }
+ String get shareButton => '[Šĥåŕé on]';
@override
- String get toastVersionRestored => '[Ṽéŕšîöñ ŕéšţöŕéð one tw]';
+ String get shareCannotShare =>
+ '[Öñļý ţĥé öŵñéŕ åñð éðîţöŕš çåñ šĥåŕé ţĥîš ƒîļé. one two three]';
@override
- String get toggleSidebar => '[Ṁåîñ ɱéñû one]';
+ String get shareDeny => '[Ðéñý on]';
@override
- String get tokenCopyOnce =>
- '[Çöþý ţĥîš ţöķéñ ñöŵ. Ýöû ŵöñ\'ţ šéé îţ åĝåîñ. one two three]';
+ String get shareFailed =>
+ '[Ţĥé šĥåŕîñĝ çĥåñĝé çöûļð ñöţ ƀé šåṽéð one two thr]';
@override
- String get tokenCreate => '[Çŕéåţé ţöķéñ one]';
+ String get shareGeneralAccess => '[Ĝéñéŕåļ åççéšš one t]';
@override
- String get tokenName => '[Ţöķéñ ñåɱé one]';
+ String shareInherited(String folder) {
+ return '[Ƒŕöɱ ƒöļðéŕ “$folder” one t]';
+ }
@override
- String get tokenRevoke => '[Ŕéṽöķé one]';
+ String get shareLinkHint =>
+ '[Åñýöñé šîĝñéð îñ ŵĥö ĥåš ţĥé ļîñķ çåñ öþéñ îţ one two three]';
@override
- String get tokenRevoked => '[ŕéṽöķéð one]';
+ String get shareMessage => '[Ṁéššåĝé one]';
@override
- String get tokensEmpty => '[Ñö ţöķéñš ýéţ. one t]';
+ String get shareNotify => '[Ñöţîƒý þéöþļé one t]';
@override
- String get tokensHint =>
- '[Þéŕšöñåļ åççéšš ţöķéñš ļéţ ṀÇÞ çļîéñţš åçţ åš ýöû. one two three f]';
+ String get sharePending => '[Þéñðîñĝ îñṽîţé one t]';
@override
- String get tokensTitle => '[Þéŕšöñåļ åççéšš ţöķéñš one two]';
+ String get sharePeopleWithAccess => '[Þéöþļé ŵîţĥ åççéšš one tw]';
@override
- String get trashedBanner => '[Ţĥîš ƒîļé îš îñ ţĥé ţŕåšĥ. one two]';
+ String get shareRemoveAccess => '[Ŕéɱöṽé åççéšš one t]';
@override
- String get typeAll => '[Åļļ ţýþéš one]';
+ String shareRemoveBody(String name) {
+ return '[$name ŵîļļ ļöšé åççéšš ţö ţĥîš ƒîļé. one two t]';
+ }
@override
- String get typeAllFiles => '[Åļļ ƒîļéš one]';
+ String shareRemoveRecipient(String email) {
+ return '[Ŕéɱöṽé $email one]';
+ }
@override
- String get typeDocuments => '[Ðöçûɱéñţš one]';
+ String get shareRemoveTitle => '[Ŕéɱöṽé åççéšš? one t]';
@override
- String get typeFiles => '[Ƒîļéš on]';
+ String shareRequestTitle(String name, String role) {
+ return '[$name ŕéǫûéšţš $role åççéšš one t]';
+ }
@override
- String get typeFolders => '[Ƒöļðéŕš one]';
+ String get shareRestricted => '[Ŕéšţŕîçţéð one]';
@override
- String get typePresentations => '[Þŕéšéñţåţîöñš one t]';
+ String get shareRestrictedHint =>
+ '[Öñļý þéöþļé ŵîţĥ åççéšš çåñ öþéñ ŵîţĥ ţĥé ļîñķ one two three]';
@override
- String get typeSpreadsheets => '[Šþŕéåðšĥééţš one t]';
+ String get shareRotateLink => '[Ŕéšéţ ļîñķ one]';
@override
- String get undoHistoryEmpty => '[Ñöţĥîñĝ ţö ûñðö one t]';
+ String shareSent(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'Šĥåŕéð ŵîţĥ $count þéöþļé',
+ one: 'Šĥåŕéð ŵîţĥ 1 þéŕšöñ',
+ );
+ return '[$_temp0 one two thre]';
+ }
@override
- String get undoHistoryTitle => '[Ûñðö ĥîšţöŕý one]';
+ String get shareSettingComments => '[Ṽîéŵéŕš çåñ šéé çöɱɱéñţš one two]';
@override
- String get untitledFolder => '[Ûñţîţļéð ƒöļðéŕ one t]';
+ String get shareSettingEditors =>
+ '[Éðîţöŕš çåñ çĥåñĝé þéŕɱîššîöñš åñð šĥåŕé one two three]';
@override
- String get versionNewAvailable => '[Å ñéŵ ṽéŕšîöñ îš åṽåîļåƀļé one two]';
+ String get shareSettingExport =>
+ '[Ṽîéŵéŕš åñð çöɱɱéñţéŕš çåñ ðöŵñļöåð, þŕîñţ åñð çöþý one two three fo]';
@override
- String get versionsEmpty => '[Ñö ṽéŕšîöñš ýéţ one t]';
+ String get shareSettingsTitle => '[Šĥåŕîñĝ šéţţîñĝš one tw]';
@override
- String get versionsName => '[Ñåɱé ţĥîš ṽéŕšîöñ one tw]';
+ String shareTitle(String name) {
+ return '[Šĥåŕé “$name” one]';
+ }
@override
- String get versionsNamedOnly => '[Öñļý ñåɱéð ṽéŕšîöñš one tw]';
+ String shareTransferBody(String name) {
+ return '[$name ƀéçöɱéš ţĥé öŵñéŕ öñçé ţĥéý åççéþţ. Ýöû\'ļļ šţåý åñ éðîţöŕ. one two three four]';
+ }
@override
- String get versionsOpenFull => '[Öþéñ ƒûļļ ĥîšţöŕý one tw]';
+ String get shareTransferOwnership => '[Ţŕåñšƒéŕ öŵñéŕšĥîþ one tw]';
@override
- String get versionsRename => '[Ŕéñåɱé ṽéŕšîöñ one t]';
+ String shareTransferSent(String name) {
+ return '[Öŵñéŕšĥîþ ţŕåñšƒéŕ šéñţ ţö $name one two t]';
+ }
@override
- String get versionsRestorePoint => '[Ɓéƒöŕé ŕéšţöŕé one t]';
+ String get shareTransferTitle => '[Ţŕåñšƒéŕ öŵñéŕšĥîþ? one two]';
@override
- String get whatsNew1 =>
- '[Öñé åççöûñţ ƒöŕ Ŵöñķý Ŵŕîţé åñð Ŵöñķý Šĥééţš — šîĝñ îñ öñçé, ûšé ƀöţĥ. one two three four f]';
+ String shareYou(String name) {
+ return '[$name (ýöû) on]';
+ }
@override
- String get whatsNew2 =>
- '[Éṽéŕý åçţîöñ îš å çöɱɱåñð: þŕéšš Çţŕļ+Šĥîƒţ+Þ (⌘⇧Þ) ţö ŕûñ åñýţĥîñĝ. one two three four fi]';
+ String get sheetsAutocomplete => '[Ƒöŕɱûļå šûĝĝéšţîöñš one two]';
@override
- String get whatsNew3 =>
- '[Ûñðö çöṽéŕš ðŕîṽé çĥåñĝéš ţöö — ŕéñåɱé, ɱöṽé, šţåŕ åñð ţŕåšĥ. one two three four]';
+ String get sheetsFormulaHelp => '[Šĥöŵ ƒöŕɱûļå ĥéļþ one tw]';
@override
- String get whatsNew4 =>
- '[Çöɱɱéñţš, ɱéñţîöñš åñð ṽéŕšîöñ ĥîšţöŕý îñ éṽéŕý ƒîļé. one two three fou]';
+ String get sheetsScripts => '[Åļļöŵ šçŕîþţš one t]';
@override
- String get whatsNewTitle => '[Ŵĥåţ\'š ñéŵ one]';
+ String shortcutConflictBody(String keys, String commands) {
+ return '[$keys îš åļŕéåðý ûšéð ƀý $commands. Ŕéþļåçé îţ? one two th]';
+ }
@override
- String get wonkyWork => '[Ŵöñķý Ŵöŕķ one]';
+ String get shortcutConflictTitle => '[Šĥöŕţçûţ îñ ûšé one t]';
@override
- String get writeAutoPair => '[Åûţö-þåîŕ ƀŕåçķéţš åñð ǫûöţéš one two th]';
+ String get shortcutReplace => '[Ŕéþļåçé one]';
@override
- String get writeExportFormat => '[Ðéƒåûļţ ðöŵñļöåð ƒöŕɱåţ one two]';
+ String get shortcutReserved =>
+ '[Ţĥé ƀŕöŵšéŕ ŕéšéŕṽéš ţĥîš šĥöŕţçûţ. one two thr]';
@override
- String get writeMarkdownShortcuts => '[Ṁåŕķðöŵñ šĥöŕţçûţš one tw]';
+ String get shortcutReset => '[Ŕéšéţ ţö ðéƒåûļţ one t]';
@override
- String get writePageless => '[Þåĝéļéšš ƀý ðéƒåûļţ one tw]';
+ String get shortcutResetAll => '[Ŕéšéţ åļļ one]';
@override
- String get writeSmartQuotes => '[Šɱåŕţ ǫûöţéš one]';
+ String get shortcutsSearch => '[Šéåŕçĥ šĥöŕţçûţš one tw]';
@override
- String get writeSpellcheck => '[Šþéļļçĥéçķ one]';
+ String get shortcutsTitle => '[Ķéýƀöåŕð šĥöŕţçûţš one tw]';
@override
- String get youHaveCommentAccess => '[Ýöû ĥåṽé çöɱɱéñţ åççéšš one two]';
+ String get someone => '[Šöɱéöñé one]';
@override
- String get youHaveViewAccess => '[Ýöû ĥåṽé ṽîéŵ åççéšš one tw]';
+ String sortBy(String field) {
+ return '[Šöŕţ: $field on]';
+ }
@override
- String get assistantNewChat => '[Ñéŵ çĥåţ one]';
+ String get sortCreated => '[Çŕéåţéð one]';
@override
- String get assistantClearChat => '[Çļéåŕ çĥåţ one]';
+ String get sortDescending => '[Ðéšçéñðîñĝ one]';
@override
- String get assistantDeleteChat => '[Ðéļéţé ţĥîš çĥåţ one t]';
+ String get sortModified => '[Ļåšţ ɱöðîƒîéð one t]';
@override
- String get assistantDeleteChatFailed => '[Çöûļðñ\'ţ ðéļéţé ţĥé çĥåţ one two]';
+ String get sortModifiedByMe => '[Ļåšţ ɱöðîƒîéð ƀý ɱé one tw]';
@override
- String get assistantAiSettings => '[ÅÎ šéţţîñĝš one]';
+ String get sortName => '[Ñåɱé on]';
@override
- String get assistantOpenAiSettings => '[Öþéñ ÅÎ šéţţîñĝš one t]';
+ String get sortOpenedByMe => '[Ļåšţ öþéñéð ƀý ɱé one t]';
@override
- String get assistantEmptyTitle =>
- '[Åšķ åƀöûţ ţĥîš ðöçûɱéñţ öŕ ţéļļ ţĥé åššîšţåñţ ŵĥåţ ţö çĥåñĝé. one two three four]';
+ String get sortSize => '[Ƒîļé šîžé one]';
@override
- String get assistantEmptyBody =>
- '[Éṽéŕý çĥåñĝé îţ ɱåķéš çåñ ƀé ûñðöñé îñ öñé šţéþ. one two three]';
+ String storageMeterValue(String usage, int percent) {
+ return '[$usage, $percent þéŕçéñţ one]';
+ }
@override
- String get assistantPlaceholder => '[Åšķ ţĥé åššîšţåñţ… one tw]';
+ String storageUsed(String size) {
+ return '[$size ûšéð on]';
+ }
@override
- String get assistantPrivacyNotice =>
- '[Ţĥé åššîšţåñţ šéñðš ţĥîš ðöçûɱéñţ åñð ýöûŕ ɱéššåĝéš ţö ţĥé ÅÎ þŕöṽîðéŕ ţö ĝéñéŕåţé åñšŵéŕš. one two three four five six]';
+ String storageUsedOf(String used, String quota) {
+ return '[$used öƒ $quota ûšéð one]';
+ }
@override
- String assistantPrivacyNoticeProvider(String provider) {
- return '[Ţĥé åššîšţåñţ šéñðš ţĥîš ðöçûɱéñţ åñð ýöûŕ ɱéššåĝéš ţö $provider ţö ĝéñéŕåţé åñšŵéŕš. one two three four five]';
+ String storageUsedOfOver(String used, String quota) {
+ return '[$used öƒ $quota ûšéð — öṽéŕ ǫûöţå one tw]';
}
@override
- String get assistantModeEdit => '[Éðîţ on]';
+ String get storageCalculating => '[Çåļçûļåţîñĝ šţöŕåĝé… one two]';
@override
- String get assistantModeEditHint =>
- '[Éðîţ ðîŕéçţļý: çĥåñĝéš ĝö šţŕåîĝĥţ îñţö ţĥé ðöçûɱéñţ one two three fo]';
+ String get storageMeterTooltip =>
+ '[Šţöŕåĝé îš šĥåŕéð ƀý Ŵöñķý Ðŕîṽé, Ŵŕîţé, Šĥééţš åñð Šļîðéš. one two three four]';
@override
- String get assistantModeEditUnavailable =>
- '[Ýöû çåñ öñļý šûĝĝéšţ çĥåñĝéš îñ ţĥîš ðöçûɱéñţ one two three]';
+ String get storageMeterTooltipOffline =>
+ '[Šţöŕåĝé îš šĥåŕéð ƀý Ŵöñķý Ðŕîṽé, Ŵŕîţé, Šĥééţš åñð Šļîðéš. (öƒƒļîñé) one two three four fi]';
@override
- String get assistantModeSuggest => '[Šûĝĝéšţ one]';
+ String storageBannerWarning(String used, String quota) {
+ return '[Ýöû\'ṽé ûšéð $used öƒ ýöûŕ $quota šţöŕåĝé. one two t]';
+ }
@override
- String get assistantModeSuggestHint =>
- '[Šûĝĝéšţ: çĥåñĝéš åŕŕîṽé åš šûĝĝéšţîöñš ţö åççéþţ öŕ ŕéĵéçţ one two three four]';
+ String get storageBannerOver =>
+ '[Ýöû\'ŕé öûţ öƒ šţöŕåĝé. Ýöû çåñ ķééþ éðîţîñĝ, ƀûţ ýöû çåñ\'ţ ûþļöåð ƒîļéš, åðð îɱåĝéš, îɱþöŕţ, öŕ ɱåķé çöþîéš ûñţîļ ýöû ƒŕéé ûþ šþåçé. one two three four five six seven eight]';
@override
- String assistantRemoveChip(String label) {
- return '[Ŕéɱöṽé $label one]';
- }
+ String get storageBannerDismiss => '[Ðîšɱîšš one]';
@override
- String get assistantThinking => '[Ţĥîñķîñĝ… one]';
+ String get quotaOwnTitle => '[Ýöû\'ŕé öûţ öƒ šţöŕåĝé one two]';
@override
- String get assistantRefused =>
- '[Ţĥé åššîšţåñţ çåñ\'ţ ĥéļþ ŵîţĥ ţĥåţ ŕéǫûéšţ. one two three]';
+ String quotaOwnBody(String needed, String available, String quota) {
+ return '[Ţĥîš ñééðš $needed, ƀûţ ýöû ĥåṽé $available ļéƒţ öƒ ýöûŕ $quota. Ƒŕéé ûþ šþåçé îñ Ŵöñķý Ðŕîṽé ƀý ðéļéţîñĝ ƒîļéš ýöû ñö ļöñĝéŕ ñééð åñð éɱþţýîñĝ ţĥé ţŕåšĥ. one two three four five six seven eig]';
+ }
@override
- String get assistantFailed => '[Šöɱéţĥîñĝ ŵéñţ ŵŕöñĝ. one two]';
+ String quotaOwnBodyUnknown(String available, String quota) {
+ return '[Ýöû ĥåṽé $available ļéƒţ öƒ ýöûŕ $quota. Ƒŕéé ûþ šþåçé îñ Ŵöñķý Ðŕîṽé ƀý ðéļéţîñĝ ƒîļéš ýöû ñö ļöñĝéŕ ñééð åñð éɱþţýîñĝ ţĥé ţŕåšĥ. one two three four five six seve]';
+ }
@override
- String get assistantStopped => '[Šţöþþéð one]';
+ String quotaOwnerTitle(String owner) {
+ return '[$owner îš öûţ öƒ šţöŕåĝé one t]';
+ }
@override
- String get assistantContinue => '[Çöñţîñûé one]';
+ String quotaOwnerImageBody(String kind, String owner) {
+ String _temp0 = intl.Intl.selectLogic(kind, {
+ 'spreadsheet':
+ 'Îɱåĝéš åððéð ţö ţĥîš šþŕéåðšĥééţ çöûñţ åĝåîñšţ ţĥé öŵñéŕ\'š šţöŕåĝé. Åšķ $owner ţö ƒŕéé ûþ šþåçé, öŕ åðð ţĥé îɱåĝé ţö å šþŕéåðšĥééţ ýöû öŵñ.',
+ 'presentation':
+ 'Îɱåĝéš åððéð ţö ţĥîš þŕéšéñţåţîöñ çöûñţ åĝåîñšţ ţĥé öŵñéŕ\'š šţöŕåĝé. Åšķ $owner ţö ƒŕéé ûþ šþåçé, öŕ åðð ţĥé îɱåĝé ţö å þŕéšéñţåţîöñ ýöû öŵñ.',
+ 'other':
+ 'Îɱåĝéš åððéð ţö ţĥîš ðöçûɱéñţ çöûñţ åĝåîñšţ ţĥé öŵñéŕ\'š šţöŕåĝé. Åšķ $owner ţö ƒŕéé ûþ šþåçé, öŕ åðð ţĥé îɱåĝé ţö å ðöçûɱéñţ ýöû öŵñ.',
+ });
+ return '[$_temp0 one two three four five six seven eight nine ten one two three four five six seven eight nine ten one two three fo]';
+ }
@override
- String get assistantUndone => '[Ûñðöñé one]';
+ String quotaOwnerBody(String owner) {
+ return '[Çöñţéñţ åððéð ţö ţĥîš ƒîļé çöûñţš åĝåîñšţ ţĥé öŵñéŕ\'š šţöŕåĝé. Åšķ $owner ţö ƒŕéé ûþ šþåçé ƒîŕšţ. one two three four five six]';
+ }
@override
- String get assistantUndoChanges => '[Ûñðö çĥåñĝéš one]';
+ String get quotaBatchTitle => '[Ñöţ éñöûĝĥ šţöŕåĝé ƒöŕ åļļ ƒîļéš one two th]';
@override
- String get assistantInsertAtCursor => '[Îñšéŕţ åţ çûŕšöŕ one t]';
+ String quotaBatchBody(int count, String needed, String available) {
+ return '[Ţĥéšé $count ƒîļéš ñééð $needed, ƀûţ ýöû ĥåṽé $available ļéƒţ. Ûþļöåð ţĥé ƒîļéš ţĥåţ ƒîţ, öŕ ƒŕéé ûþ šþåçé ƒîŕšţ. one two three four five s]';
+ }
@override
- String get assistantAllow => '[Åļļöŵ on]';
+ String get quotaBatchFit => '[Ûþļöåð ţĥé ƒîļéš ţĥåţ ƒîţ one two]';
@override
- String get assistantDeny => '[Ðéñý on]';
+ String quotaTransferTitle(String name) {
+ return '[$name ðöéšñ\'ţ ĥåṽé éñöûĝĥ šţöŕåĝé one two t]';
+ }
@override
- String get assistantShow => '[Šĥöŵ on]';
+ String quotaTransferBody(String name, String needed, String item) {
+ return '[$name ñééðš $needed ƒŕéé ţö ţåķé öŵñéŕšĥîþ öƒ “$item”. Åšķ ţĥéɱ ţö ƒŕéé ûþ šþåçé ƒîŕšţ. one two three four f]';
+ }
@override
- String get assistantCopyCode => '[Çöþý çöðé one]';
+ String get quotaTransferAcceptTitle =>
+ '[Ýöû ðöñ\'ţ ĥåṽé éñöûĝĥ šţöŕåĝé one two t]';
@override
- String get aiNotConfigured =>
- '[ÅÎ îšñ\'ţ çöñƒîĝûŕéð öñ ţĥîš šéŕṽéŕ. one two thr]';
+ String quotaTransferAcceptBody(String item, String needed, String available) {
+ return '[Ţåķîñĝ öŵñéŕšĥîþ öƒ “$item” ñééðš $needed, ƀûţ ýöû ĥåṽé $available ļéƒţ. one two three]';
+ }
@override
- String get aiNotConfiguredHint =>
- '[Åðð ýöûŕ öŵñ ÖþéñÅÎ, Åñţĥŕöþîç öŕ Ĝéɱîñî ÅÞÎ ķéý ţö ûšé îţ, öŕ åšķ åñ åðɱîñîšţŕåţöŕ ţö åðð öñé ţö ţĥé šéŕṽéŕ. one two three four five six sev]';
+ String get quotaTooLargeTitle => '[Ţĥîš ƒîļé îš ţöö ļåŕĝé one two]';
@override
- String get aiNotConfiguredAdminHint =>
- '[Åñ åðɱîñîšţŕåţöŕ çåñ éñåƀļé îţ ƀý åððîñĝ åñ ÖþéñÅÎ, Åñţĥŕöþîç öŕ Ĝéɱîñî ÅÞÎ ķéý ţö ţĥé šéŕṽéŕ çöñƒîĝûŕåţîöñ. one two three four five six seve]';
+ String quotaTooLargeBody(String max) {
+ return '[Ƒîļéš çåñ ƀé åţ ɱöšţ $max. one tw]';
+ }
@override
- String get aiAddApiKey => '[Åðð åñ ÅÞÎ ķéý one]';
+ String quotaTooLargeNowBody(String max) {
+ return '[Ƒîļéš çåñ ƀé åţ ɱöšţ $max ŕîĝĥţ ñöŵ, ƀéçåûšé ţĥåţ\'š ĥöŵ ɱûçĥ šţöŕåĝé ýöû ĥåṽé ļéƒţ. one two three four five]';
+ }
@override
- String get aiDisabled => '[ÅÎ îš ţûŕñéð öƒƒ öñ ţĥîš šéŕṽéŕ. one two th]';
+ String get quotaItemFallback => '[ţĥîš îţéɱ one]';
@override
- String get aiOptedOut =>
- '[Ţĥé åššîšţåñţ îš ţûŕñéð öƒƒ îñ ýöûŕ šéţţîñĝš (Šéţţîñĝš › ÅÎ). one two three four]';
+ String get strengthEmpty => '[Éñţéŕ å þåššŵöŕð one t]';
@override
- String get aiAssistantTitle => '[Åššîšţåñţ one]';
+ String get strengthFair => '[Ƒåîŕ on]';
@override
- String get aiAssistantHint =>
- '[Ţĥé åššîšţåñţ ĥéļþš ýöû ŵŕîţé, éðîţ åñð ûñðéŕšţåñð ðöçûɱéñţš. Îţ öñļý šééš ţĥé ðöçûɱéñţ ýöû ûšé îţ îñ. one two three four five six se]';
+ String get strengthGood => '[Ĝööð on]';
@override
- String get aiEnableHint =>
- '[Ţûŕñ öƒƒ ţö ĥîðé ÅÎ ƒéåţûŕéš åñð šţöþ šéñðîñĝ çöñţéñţ ţö ţĥé ÅÎ šéŕṽîçé. one two three four fi]';
+ String strengthLabel(String label) {
+ return '[Þåššŵöŕð šţŕéñĝţĥ: $label one tw]';
+ }
@override
- String get aiQuality => '[ÅÎ ǫûåļîţý one]';
+ String get strengthStrong => '[Šţŕöñĝ one]';
@override
- String get aiQualityBest => '[Ɓéšţ on]';
+ String get strengthWeak => '[Ŵéåķ on]';
@override
- String get aiQualityBestHint => '[Ṁöšţ çåþåƀļé åñšŵéŕš åñð éðîţš one two th]';
+ String get switchAccount => '[Šŵîţçĥ åççöûñţ one t]';
@override
- String get aiQualityBalanced => '[Ɓåļåñçéð one]';
+ String get templateBlank => '[Ɓļåñķ on]';
@override
- String get aiQualityBalancedHint =>
- '[Ĝööð åñšŵéŕš, å ļîţţļé ƒåšţéŕ one two t]';
+ String get templateEducation => '[Éðûçåţîöñ one]';
@override
- String get aiQualityFast => '[Ƒåšţ on]';
+ String get templateFinance => '[Ƒîñåñçé one]';
@override
- String get aiQualityFastHint =>
- '[Ǫûîçķéšţ åñšŵéŕš ƒöŕ šîɱþļé ţåšķš one two thr]';
+ String get templateGallery => '[Ţéɱþļåţé ĝåļļéŕý one tw]';
@override
- String get aiQualitySaveFailed => '[Çöûļðñ\'ţ šåṽé ţĥé ÅÎ ǫûåļîţý one two t]';
+ String get templatePersonal => '[Þéŕšöñåļ one]';
@override
- String get aiUsageToday => '[Ûšåĝé ţöðåý one]';
+ String get templateProjectManagement => '[Þŕöĵéçţ ɱåñåĝéɱéñţ one tw]';
@override
- String get aiUsageTodayHint => '[Ŕéšéţš ðåîļý åţ ɱîðñîĝĥţ ÛŢÇ. one two t]';
+ String templateStripTitle(String kind) {
+ return '[Šţåŕţ å ñéŵ $kind one]';
+ }
@override
- String aiUsagePercent(int percent) {
- return '[$percent % o]';
- }
+ String get templateWork => '[Ŵöŕķ on]';
@override
- String get aiProviderOpenai => '[ÖþéñÅÎ one]';
+ String get templatesHide => '[Ĥîðé ţéɱþļåţéš one t]';
@override
- String get aiProviderAnthropic => '[Åñţĥŕöþîç one]';
+ String get templatesShow => '[Šĥöŵ ţéɱþļåţéš one t]';
@override
- String get aiProviderGemini => '[Ĝööĝļé Ĝéɱîñî one t]';
+ String get themeDark => '[Ðåŕķ on]';
@override
- String get aiProvidersServerTitle => '[Þŕöṽîðéð ƀý ţĥîš šéŕṽéŕ one two]';
+ String get themeLight => '[Ļîĝĥţ on]';
@override
- String aiProvidersServerBody(String provider) {
- return '[ÅÎ ŕéǫûéšţš ĝö ţö $provider ŵîţĥ ţĥîš šéŕṽéŕ\'š ÅÞÎ ķéý. Ýöû ðöñ\'ţ ñééð å ķéý öƒ ýöûŕ öŵñ. one two three four five]';
+ String get themeSystem => '[Šýšţéɱ one]';
+
+ @override
+ String themeToggleTooltip(String mode) {
+ return '[Ţĥéɱé: $mode one]';
}
@override
- String get aiProvidersKeyTitle => '[Ýöûŕ ÅÞÎ ķéý one]';
+ String timeHoursAgo(int hours) {
+ return '[$hours ĥ åĝö on]';
+ }
@override
- String get aiProvidersKeyHint =>
- '[Ţĥîš šéŕṽéŕ ĥåš ñö ÅÎ þŕöṽîðéŕ šéţ ûþ. Åðð ýöûŕ öŵñ ķéý ţö ûšé ţĥé åššîšţåñţ åñð ţĥé öţĥéŕ ÅÎ ƒéåţûŕéš. Ţĥé ķéý îš šţöŕéð öñ ţĥé šéŕṽéŕ, ûšéð öñļý ƒöŕ ýöûŕ ŕéǫûéšţš åñð ñéṽéŕ šĥöŵñ åĝåîñ. one two three four five six seven eight nine ten one]';
+ String get timeJustNow => '[Ĵûšţ ñöŵ one]';
@override
- String get aiProvidersProvider => '[Þŕöṽîðéŕ one]';
+ String timeMinutesAgo(int minutes) {
+ return '[$minutes ɱîñ åĝö one]';
+ }
@override
- String get aiProvidersKey => '[ÅÞÎ ķéý one]';
+ String get toastAccessRequested => '[Ŕéǫûéšţ šéñţ one]';
@override
- String get aiProvidersKeyPlaceholder => '[Þåšţé ýöûŕ ÅÞÎ ķéý one tw]';
+ String toastCopyCreated(String name) {
+ return '[Çŕéåţéð “$name” one]';
+ }
@override
- String get aiProvidersSave => '[Šåṽé ķéý one]';
+ String get toastLinkCopied => '[Ļîñķ çöþîéð one]';
@override
- String get aiProvidersSaved => '[ÅÞÎ ķéý šåṽéð one]';
+ String get toastMcpConfigCopied => '[ṀÇÞ šéţţîñĝš çöþîéð one tw]';
@override
- String get aiProvidersSaveFailed => '[Çöûļðñ\'ţ šåṽé ţĥé ÅÞÎ ķéý one two]';
+ String toastMovedToTrash(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count îţéɱš ɱöṽéð ţö ţŕåšĥ',
+ one: 'Ṁöṽéð ţö ţŕåšĥ',
+ );
+ return '[$_temp0 one two thr]';
+ }
@override
- String get aiProvidersCurrent => '[Ýöûŕ ķéý one]';
+ String get toastOfflineOpFailed =>
+ '[Å çĥåñĝé ɱåðé öƒƒļîñé çöûļð ñöţ ƀé åþþļîéð one two three]';
@override
- String aiProvidersCurrentValue(String provider, String last4) {
- return '[$provider ķéý éñðîñĝ îñ $last4 one]';
+ String get toastShortcutAdded => '[Šĥöŕţçûţ åððéð one t]';
+
+ @override
+ String get toastUndoPartial =>
+ '[Šöɱé çĥåñĝéš çöûļðñ\'ţ ƀé ûñðöñé ƀéçåûšé ţĥéý ŵéŕé éðîţéð ƀý šöɱéöñé éļšé. one two three four fiv]';
+
+ @override
+ String toastVersionNamed(String name) {
+ return '[Šåṽéð ṽéŕšîöñ “$name” one t]';
}
@override
- String get aiProvidersRemove => '[Ŕéɱöṽé ķéý one]';
+ String get toastVersionRestored => '[Ṽéŕšîöñ ŕéšţöŕéð one tw]';
@override
- String get aiProvidersRemoveTitle => '[Ŕéɱöṽé ýöûŕ ÅÞÎ ķéý? one tw]';
+ String get toggleSidebar => '[Ṁåîñ ɱéñû one]';
@override
- String get aiProvidersRemoveBody =>
- '[ÅÎ ƒéåţûŕéš šţöþ ŵöŕķîñĝ ƒöŕ ýöû ûñţîļ ýöû åðð å ķéý åĝåîñ. one two three fou]';
+ String get tokenCopyOnce =>
+ '[Çöþý ţĥîš ţöķéñ ñöŵ. Ýöû ŵöñ\'ţ šéé îţ åĝåîñ. one two three]';
@override
- String get aiProvidersRemoved => '[ÅÞÎ ķéý ŕéɱöṽéð one t]';
+ String get tokenCreate => '[Çŕéåţé ţöķéñ one]';
@override
- String get aiProvidersRemoveFailed =>
- '[Çöûļðñ\'ţ ŕéɱöṽé ţĥé ÅÞÎ ķéý one two t]';
+ String get tokenName => '[Ţöķéñ ñåɱé one]';
@override
- String get aiProvidersNotAllowed =>
- '[Ţĥîš šéŕṽéŕ ðöéšñ\'ţ åļļöŵ þéŕšöñåļ ÅÞÎ ķéýš. Åšķ åñ åðɱîñîšţŕåţöŕ ţö šéţ ûþ ÅÎ. one two three four five]';
+ String get tokenRevoke => '[Ŕéṽöķé one]';
@override
- String get aiProvidersManage => '[Ṁåñåĝé ÅÎ þŕöṽîðéŕš one tw]';
+ String get tokenRevoked => '[ŕéṽöķéð one]';
@override
- String get aiChatHistory => '[Çĥåţ ĥîšţöŕý one]';
+ String get tokensEmpty => '[Ñö ţöķéñš ýéţ. one t]';
@override
- String get aiChatHistoryHint =>
- '[Ðéļéţé åļļ ýöûŕ åššîšţåñţ çĥåţš. one two th]';
+ String get tokensHint =>
+ '[Þéŕšöñåļ åççéšš ţöķéñš ļéţ ṀÇÞ çļîéñţš åçţ åš ýöû. one two three f]';
@override
- String get aiClearHistory => '[Çļéåŕ ĥîšţöŕý one t]';
+ String get tokensTitle => '[Þéŕšöñåļ åççéšš ţöķéñš one two]';
@override
- String get aiClearHistoryTitle => '[Çļéåŕ åššîšţåñţ ĥîšţöŕý? one two]';
+ String get trashedBanner => '[Ţĥîš ƒîļé îš îñ ţĥé ţŕåšĥ. one two]';
@override
- String get aiClearHistoryBody =>
- '[Åļļ ýöûŕ åššîšţåñţ çĥåţš îñ éṽéŕý ðöçûɱéñţ åŕé ðéļéţéð. Ţĥîš çåñ\'ţ ƀé ûñðöñé. Éðîţš ţĥé åššîšţåñţ ɱåðé šţåý îñ ţĥé ðöçûɱéñţš. one two three four five six seven eig]';
+ String get typeAll => '[Åļļ ţýþéš one]';
@override
- String get aiClearHistoryFailed => '[Çöûļðñ\'ţ çļéåŕ ţĥé ĥîšţöŕý one two t]';
+ String get typeAllFiles => '[Åļļ ƒîļéš one]';
@override
- String aiHistoryCleared(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: 'Ðéļéţéð $count çĥåţš',
- one: 'Ðéļéţéð 1 çĥåţ',
- );
- return '[$_temp0 one two t]';
- }
+ String get typeDocuments => '[Ðöçûɱéñţš one]';
@override
- String get mcpScopeDriveRead =>
- '[Ðŕîṽé: ŕéåð (ļîšţ, šéåŕçĥ, éẋþöŕţ, ðöŵñļöåð ƒîļéš, šţöŕåĝé) one two three four]';
+ String get typeFiles => '[Ƒîļéš on]';
@override
- String get mcpScopeDriveWrite =>
- '[Ðŕîṽé: ŵŕîţé (çŕéåţé, ŕéñåɱé, ɱöṽé, ţŕåšĥ, šĥåŕé, ûþļöåð åñð çöñṽéŕţ ƒîļéš) one two three four five]';
+ String get typeFolders => '[Ƒöļðéŕš one]';
@override
- String get mcpScopeWriteRead => '[Ðöçûɱéñţš: ŕéåð one t]';
+ String get typePresentations => '[Þŕéšéñţåţîöñš one t]';
@override
- String get mcpScopeWriteEdit =>
- '[Ðöçûɱéñţš: éðîţ (çöñţéñţ, çöɱɱéñţš, šûĝĝéšţîöñš) one two three fo]';
+ String get typeSpreadsheets => '[Šþŕéåðšĥééţš one t]';
@override
- String get mcpScopeSheetsRead => '[Šþŕéåðšĥééţš: ŕéåð one tw]';
+ String get undoHistoryEmpty => '[Ñöţĥîñĝ ţö ûñðö one t]';
@override
- String get mcpScopeSheetsEdit => '[Šþŕéåðšĥééţš: éðîţ one tw]';
+ String get undoHistoryTitle => '[Ûñðö ĥîšţöŕý one]';
@override
- String get mcpScopeSlidesRead => '[Þŕéšéñţåţîöñš: ŕéåð one two]';
+ String get untitledFolder => '[Ûñţîţļéð ƒöļðéŕ one t]';
@override
- String get mcpScopeSlidesEdit => '[Þŕéšéñţåţîöñš: éðîţ one two]';
+ String get versionNewAvailable => '[Å ñéŵ ṽéŕšîöñ îš åṽåîļåƀļé one two]';
@override
- String get mcpScopeScriptsRun => '[Šçŕîþţš: ŕûñ one]';
+ String get versionsEmpty => '[Ñö ṽéŕšîöñš ýéţ one t]';
@override
- String get mcpScopeUiControl =>
- '[Çöñţŕöļ åþþ ŵîñðöŵš (ûî.* ţööļš) one two th]';
+ String get versionsName => '[Ñåɱé ţĥîš ṽéŕšîöñ one tw]';
@override
- String get mcpScopeBoard =>
- '[Ɓûĝš & îɱþŕöṽéɱéñţš: ŕéåð, ŕéþöŕţ îššûéš, ṽöţé, çļåîɱ ţåšķš one two three four]';
+ String get versionsNamedOnly => '[Öñļý ñåɱéð ṽéŕšîöñš one tw]';
@override
- String get tokenCreateHint =>
- '[Ţĥé ţöķéñ åçţš åš ýöû ŵîţĥ ţĥé þéŕɱîššîöñš ýöû çĥööšé. Ýöû çåñ ŕéṽöķé îţ åţ åñý ţîɱé. one two three four five s]';
+ String get versionsOpenFull => '[Öþéñ ƒûļļ ĥîšţöŕý one tw]';
@override
- String get tokenNameRequired => '[Ĝîṽé ţĥé ţöķéñ å ñåɱé. one two]';
+ String get versionsRename => '[Ŕéñåɱé ṽéŕšîöñ one t]';
@override
- String get tokenScopesRequired =>
- '[Çĥööšé åţ ļéåšţ öñé þéŕɱîššîöñ. one two th]';
+ String get versionsRestorePoint => '[Ɓéƒöŕé ŕéšţöŕé one t]';
@override
- String get tokenExpires => '[Éẋþîŕéš one]';
+ String get whatsNew1 =>
+ '[Öñé åççöûñţ ƒöŕ Ŵöñķý Ŵŕîţé åñð Ŵöñķý Šĥééţš — šîĝñ îñ öñçé, ûšé ƀöţĥ. one two three four f]';
@override
- String tokenExpiresInDays(int days) {
- String _temp0 = intl.Intl.pluralLogic(
- days,
- locale: localeName,
- other: 'Îñ $days ðåýš',
- one: 'Îñ 1 ðåý',
- );
- return '[$_temp0 one t]';
- }
+ String get whatsNew2 =>
+ '[Éṽéŕý åçţîöñ îš å çöɱɱåñð: þŕéšš Çţŕļ+Šĥîƒţ+Þ (⌘⇧Þ) ţö ŕûñ åñýţĥîñĝ. one two three four fi]';
@override
- String get tokenExpiresInYear => '[Îñ 1 ýéåŕ one]';
+ String get whatsNew3 =>
+ '[Ûñðö çöṽéŕš ðŕîṽé çĥåñĝéš ţöö — ŕéñåɱé, ɱöṽé, šţåŕ åñð ţŕåšĥ. one two three four]';
@override
- String get tokenNever => '[Ñéṽéŕ on]';
+ String get whatsNew4 =>
+ '[Çöɱɱéñţš, ɱéñţîöñš åñð ṽéŕšîöñ ĥîšţöŕý îñ éṽéŕý ƒîļé. one two three fou]';
@override
- String get tokenNeverLower => '[ñéṽéŕ on]';
+ String get whatsNewTitle => '[Ŵĥåţ\'š ñéŵ one]';
@override
- String get tokenExpired => '[éẋþîŕéð one]';
+ String get wonkyWork => '[Ŵöñķý Ŵöŕķ one]';
@override
- String tokenNameWithStatus(String name, String status) {
- return '[$name · $status o]';
- }
+ String get writeAutoPair => '[Åûţö-þåîŕ ƀŕåçķéţš åñð ǫûöţéš one two th]';
@override
- String tokenDates(String created, String expires, String lastUsed) {
- return '[Çŕéåţéð $created · éẋþîŕéš $expires · ļåšţ ûšéð $lastUsed one two t]';
- }
+ String get writeExportFormat => '[Ðéƒåûļţ ðöŵñļöåð ƒöŕɱåţ one two]';
@override
- String tokenRevokeTitle(String name) {
- return '[Ŕéṽöķé \"$name\"? one]';
- }
+ String get writeMarkdownShortcuts => '[Ṁåŕķðöŵñ šĥöŕţçûţš one tw]';
@override
- String get tokenRevokeBody =>
- '[ṀÇÞ çļîéñţš ûšîñĝ ţĥîš ţöķéñ šţöþ ŵöŕķîñĝ îɱɱéðîåţéļý. Ţĥîš çåñ\'ţ ƀé ûñðöñé. one two three four five]';
+ String get writePageless => '[Þåĝéļéšš ƀý ðéƒåûļţ one tw]';
@override
- String get toastTokenCopied => '[Ţöķéñ çöþîéð one]';
+ String get writeSmartQuotes => '[Šɱåŕţ ǫûöţéš one]';
@override
- String mcpEndpoint(String url) {
- return '[Éñðþöîñţ: $url one]';
- }
+ String get writeSpellcheck => '[Šþéļļçĥéçķ one]';
@override
- String mcpReplaceToken(String variable) {
- return '[Ŕéþļåçé $variable ŵîţĥ å ţöķéñ. one two]';
- }
+ String get youHaveCommentAccess => '[Ýöû ĥåṽé çöɱɱéñţ åççéšš one two]';
@override
- String get mcpWindowControl => '[Ŵîñðöŵ çöñţŕöļ one t]';
+ String get youHaveViewAccess => '[Ýöû ĥåṽé ṽîéŵ åççéšš one tw]';
@override
- String get mcpWindowControlHint =>
- '[Ļéţš ṀÇÞ çļîéñţš ŵîţĥ ţĥé ûî:çöñţŕöļ þéŕɱîššîöñ ðŕîṽé ţĥîš ŵîñðöŵ: ñåṽîĝåţé, ŕûñ çöɱɱåñðš, ƒîļļ ðîåļöĝš, ţåķé šçŕééñšĥöţš. Å þîļļ ŵîţĥ Šţöþ šĥöŵš ŵĥîļé åñ åĝéñţ ŵöŕķš. one two three four five six seven eight nine ten o]';
+ String get assistantNewChat => '[Ñéŵ çĥåţ one]';
@override
- String get mcpBridgeStatus => '[Šţåţûš one]';
+ String get assistantClearChat => '[Çļéåŕ çĥåţ one]';
@override
- String get mcpBridgeOff => '[Öƒƒ on]';
+ String get assistantDeleteChat => '[Ðéļéţé ţĥîš çĥåţ one t]';
@override
- String get mcpBridgeConnecting => '[Çöññéçţîñĝ… one]';
+ String get assistantDeleteChatFailed => '[Çöûļðñ\'ţ ðéļéţé ţĥé çĥåţ one two]';
@override
- String mcpBridgeConnected(String id) {
- return '[Öñ · ŵîñðöŵ $id one]';
- }
+ String get assistantAiSettings => '[ÅÎ šéţţîñĝš one]';
@override
- String driveItemLabel(String kind, String name) {
- return '[$kind $name]';
- }
+ String get assistantOpenAiSettings => '[Öþéñ ÅÎ šéţţîñĝš one t]';
@override
- String get activityShowMore => '[Šĥöŵ ɱöŕé one]';
+ String get assistantEmptyTitle =>
+ '[Åšķ åƀöûţ ţĥîš ðöçûɱéñţ öŕ ţéļļ ţĥé åššîšţåñţ ŵĥåţ ţö çĥåñĝé. one two three four]';
@override
- String get notificationsFilterAll => '[Åļļ on]';
+ String get assistantEmptyBody =>
+ '[Éṽéŕý çĥåñĝé îţ ɱåķéš çåñ ƀé ûñðöñé îñ öñé šţéþ. one two three]';
@override
- String get notificationsFilterUnread => '[Ûñŕéåð one]';
+ String get assistantPlaceholder => '[Åšķ ţĥé åššîšţåñţ… one tw]';
@override
- String notificationsFilterUnreadCount(int count) {
- return '[Ûñŕéåð ($count) one]';
+ String get assistantPrivacyNotice =>
+ '[Ţĥé åššîšţåñţ šéñðš ţĥîš ðöçûɱéñţ åñð ýöûŕ ɱéššåĝéš ţö ţĥé ÅÎ þŕöṽîðéŕ ţö ĝéñéŕåţé åñšŵéŕš. one two three four five six]';
+
+ @override
+ String assistantPrivacyNoticeProvider(String provider) {
+ return '[Ţĥé åššîšţåñţ šéñðš ţĥîš ðöçûɱéñţ åñð ýöûŕ ɱéššåĝéš ţö $provider ţö ĝéñéŕåţé åñšŵéŕš. one two three four five]';
}
@override
- String get notificationsSettings => '[Ñöţîƒîçåţîöñ šéţţîñĝš one two]';
+ String get assistantModeEdit => '[Éðîţ on]';
@override
- String get notificationsLoadFailed =>
- '[Çöûļðñ\'ţ ļöåð ýöûŕ ñöţîƒîçåţîöñš one two thr]';
+ String get assistantModeEditHint =>
+ '[Éðîţ ðîŕéçţļý: çĥåñĝéš ĝö šţŕåîĝĥţ îñţö ţĥé ðöçûɱéñţ one two three fo]';
@override
- String get notificationsEmptyUnread => '[Ñö ûñŕéåð ñöţîƒîçåţîöñš one two]';
+ String get assistantModeEditUnavailable =>
+ '[Ýöû çåñ öñļý šûĝĝéšţ çĥåñĝéš îñ ţĥîš ðöçûɱéñţ one two three]';
@override
- String get notificationsLoadMore => '[Ļöåð ɱöŕé one]';
+ String get assistantModeSuggest => '[Šûĝĝéšţ one]';
@override
- String get notificationsUnreadMarker => '[ûñŕéåð one]';
+ String get assistantModeSuggestHint =>
+ '[Šûĝĝéšţ: çĥåñĝéš åŕŕîṽé åš šûĝĝéšţîöñš ţö åççéþţ öŕ ŕéĵéçţ one two three four]';
@override
- String notifSuggestionAccepted(String who, String item) {
- return '[$who åççéþţéð ýöûŕ šûĝĝéšţîöñ îñ $item one two t]';
+ String assistantRemoveChip(String label) {
+ return '[Ŕéɱöṽé $label one]';
}
@override
- String notifSuggestionRejected(String who, String item) {
- return '[$who ŕéĵéçţéð ýöûŕ šûĝĝéšţîöñ îñ $item one two t]';
- }
+ String get assistantThinking => '[Ţĥîñķîñĝ… one]';
@override
- String get notifyAccessRequests => '[Åççéšš ŕéǫûéšţš one t]';
+ String get assistantRefused =>
+ '[Ţĥé åššîšţåñţ çåñ\'ţ ĥéļþ ŵîţĥ ţĥåţ ŕéǫûéšţ. one two three]';
@override
- String get notifySuggestions => '[Šûĝĝéšţîöñš öñ ɱý ƒîļéš one two]';
+ String get assistantFailed => '[Šöɱéţĥîñĝ ŵéñţ ŵŕöñĝ. one two]';
@override
- String get notifyDigest => '[Ɓûñðļé éɱåîļš one t]';
+ String get assistantStopped => '[Šţöþþéð one]';
@override
- String get notifyDigestHint =>
- '[Šéñð öñé éɱåîļ éṽéŕý 15 ɱîñûţéš îñšţéåð öƒ öñé þéŕ ñöţîƒîçåţîöñ. Ṁéñţîöñš åñð åššîĝñɱéñţš åŕé åļŵåýš éɱåîļéð ŕîĝĥţ åŵåý. one two three four five six seven ei]';
+ String get assistantContinue => '[Çöñţîñûé one]';
@override
- String get exportFormatMarkdown => '[Ṁåŕķðöŵñ (.ɱð) one t]';
+ String get assistantUndone => '[Ûñðöñé one]';
@override
- String get exportFormatHtml => '[ĤŢṀĻ (.ĥţɱļ) one]';
+ String get assistantUndoChanges => '[Ûñðö çĥåñĝéš one]';
@override
- String get exportFormatPdf => '[ÞÐƑ (.þðƒ) one]';
+ String get assistantInsertAtCursor => '[Îñšéŕţ åţ çûŕšöŕ one t]';
@override
- String get exportFormatText => '[Þļåîñ ţéẋţ (.ţẋţ) one tw]';
+ String get assistantAllow => '[Åļļöŵ on]';
@override
- String get badgePendingCreate =>
- '[Çŕéåţéð öƒƒļîñé. Îţ îš åððéð ţö ýöûŕ Ðŕîṽé ŵĥéñ ýöû\'ŕé ƀåçķ öñļîñé. one two three four f]';
+ String get assistantDeny => '[Ðéñý on]';
@override
- String dropToUploadInto(String folder) {
- return '[Ðŕöþ ƒîļéš ţö ûþļöåð ţö $folder one two]';
- }
+ String get assistantShow => '[Šĥöŵ on]';
@override
- String dropUploadToFolder(String folder) {
- return '[Ûþļöåð ţö $folder one]';
- }
+ String get assistantCopyCode => '[Çöþý çöðé one]';
@override
- String get driveItemUploading => '[Ûþļöåðîñĝ… one]';
+ String get aiNotConfigured =>
+ '[ÅÎ îšñ\'ţ çöñƒîĝûŕéð öñ ţĥîš šéŕṽéŕ. one two thr]';
@override
- String get spellingTitle => '[Šþéļļîñĝ one]';
+ String get aiNotConfiguredHint =>
+ '[Åðð ýöûŕ öŵñ ÖþéñÅÎ, Åñţĥŕöþîç öŕ Ĝéɱîñî ÅÞÎ ķéý ţö ûšé îţ, öŕ åšķ åñ åðɱîñîšţŕåţöŕ ţö åðð öñé ţö ţĥé šéŕṽéŕ. one two three four five six sev]';
@override
- String get spellingDone => '[Ðöñé on]';
+ String get aiNotConfiguredAdminHint =>
+ '[Åñ åðɱîñîšţŕåţöŕ çåñ éñåƀļé îţ ƀý åððîñĝ åñ ÖþéñÅÎ, Åñţĥŕöþîç öŕ Ĝéɱîñî ÅÞÎ ķéý ţö ţĥé šéŕṽéŕ çöñƒîĝûŕåţîöñ. one two three four five six seve]';
@override
- String get spellingNoErrors => '[Ñö šþéļļîñĝ éŕŕöŕš ƒöûñð. one two]';
+ String get aiAddApiKey => '[Åðð åñ ÅÞÎ ķéý one]';
@override
- String get spellingUnavailable =>
- '[Šþéļļçĥéçķ îšñ’ţ åṽåîļåƀļé ƒöŕ ţĥîš ðöçûɱéñţ’š ļåñĝûåĝé. one two three four]';
+ String get aiDisabled => '[ÅÎ îš ţûŕñéð öƒƒ öñ ţĥîš šéŕṽéŕ. one two th]';
@override
- String get spellingNotInDictionary => '[Ñöţ îñ ðîçţîöñåŕý one tw]';
+ String get aiOptedOut =>
+ '[Ţĥé åššîšţåñţ îš ţûŕñéð öƒƒ îñ ýöûŕ šéţţîñĝš (Šéţţîñĝš › ÅÎ). one two three four]';
@override
- String get spellingSuggestions => '[Šûĝĝéšţîöñš one]';
+ String get aiAssistantTitle => '[Åššîšţåñţ one]';
@override
- String get spellingNoSuggestions => '[Ñö šûĝĝéšţîöñš one t]';
+ String get aiAssistantHint =>
+ '[Ţĥé åššîšţåñţ ĥéļþš ýöû ŵŕîţé, éðîţ åñð ûñðéŕšţåñð ðöçûɱéñţš. Îţ öñļý šééš ţĥé ðöçûɱéñţ ýöû ûšé îţ îñ. one two three four five six se]';
@override
- String get spellingChange => '[Çĥåñĝé one]';
+ String get aiEnableHint =>
+ '[Ţûŕñ öƒƒ ţö ĥîðé ÅÎ ƒéåţûŕéš åñð šţöþ šéñðîñĝ çöñţéñţ ţö ţĥé ÅÎ šéŕṽîçé. one two three four fi]';
@override
- String get spellingChangeAll => '[Çĥåñĝé åļļ one]';
+ String get aiQuality => '[ÅÎ ǫûåļîţý one]';
@override
- String get spellingIgnore => '[Îĝñöŕé one]';
+ String get aiQualityBest => '[Ɓéšţ on]';
@override
- String get spellingIgnoreAll => '[Îĝñöŕé åļļ one]';
+ String get aiQualityBestHint => '[Ṁöšţ çåþåƀļé åñšŵéŕš åñð éðîţš one two th]';
@override
- String get spellingAddToDictionary => '[Åðð ţö ðîçţîöñåŕý one tw]';
+ String get aiQualityBalanced => '[Ɓåļåñçéð one]';
@override
- String get spellingPersonalDictionary => '[Þéŕšöñåļ ðîçţîöñåŕý one two]';
+ String get aiQualityBalancedHint =>
+ '[Ĝööð åñšŵéŕš, å ļîţţļé ƒåšţéŕ one two t]';
@override
- String get spellingPersonalDictionaryHint =>
- '[Ŵöŕðš ýöû åððéð åŕé ñéṽéŕ ɱåŕķéð åš ɱîššþéļļéð, îñ åñý ðöçûɱéñţ. one two three four]';
+ String get aiQualityFast => '[Ƒåšţ on]';
@override
- String get spellingAddWord => '[Åðð å ŵöŕð one]';
+ String get aiQualityFastHint =>
+ '[Ǫûîçķéšţ åñšŵéŕš ƒöŕ šîɱþļé ţåšķš one two thr]';
@override
- String get spellingAdd => '[Åðð on]';
+ String get aiQualitySaveFailed => '[Çöûļðñ\'ţ šåṽé ţĥé ÅÎ ǫûåļîţý one two t]';
@override
- String get spellingNoWords => '[Ñö ŵöŕðš ýéţ. one]';
+ String get aiUsageToday => '[Ûšåĝé ţöðåý one]';
@override
- String spellingRemoveWord(String word) {
- return '[Ŕéɱöṽé $word one]';
+ String get aiUsageTodayHint => '[Ŕéšéţš ðåîļý åţ ɱîðñîĝĥţ ÛŢÇ. one two t]';
+
+ @override
+ String aiUsagePercent(int percent) {
+ return '[$percent % o]';
}
@override
- String get emojiPickerClose => '[Çļöšé éɱöĵî þîçķéŕ one tw]';
+ String get aiProviderOpenai => '[ÖþéñÅÎ one]';
@override
- String get emojiDialogLabel => '[Éɱöĵî on]';
+ String get aiProviderAnthropic => '[Åñţĥŕöþîç one]';
@override
- String get emojiPickerLabel => '[Éɱöĵî þîçķéŕ one]';
+ String get aiProviderGemini => '[Ĝööĝļé Ĝéɱîñî one t]';
@override
- String get emojiSearch => '[Šéåŕçĥ éɱöĵî one]';
+ String get aiProvidersServerTitle => '[Þŕöṽîðéð ƀý ţĥîš šéŕṽéŕ one two]';
@override
- String get emojiRecentlyUsed => '[Ŕéçéñţļý ûšéð one t]';
+ String aiProvidersServerBody(String provider) {
+ return '[ÅÎ ŕéǫûéšţš ĝö ţö $provider ŵîţĥ ţĥîš šéŕṽéŕ\'š ÅÞÎ ķéý. Ýöû ðöñ\'ţ ñééð å ķéý öƒ ýöûŕ öŵñ. one two three four five]';
+ }
@override
- String get emojiLoadFailed => '[Éɱöĵî çöûļð ñöţ ƀé ļöåðéð one two]';
+ String get aiProvidersKeyTitle => '[Ýöûŕ ÅÞÎ ķéý one]';
@override
- String get emojiNoneFound => '[Ñö éɱöĵî ƒöûñð one t]';
+ String get aiProvidersKeyHint =>
+ '[Ţĥîš šéŕṽéŕ ĥåš ñö ÅÎ þŕöṽîðéŕ šéţ ûþ. Åðð ýöûŕ öŵñ ķéý ţö ûšé ţĥé åššîšţåñţ åñð ţĥé öţĥéŕ ÅÎ ƒéåţûŕéš. Ţĥé ķéý îš šţöŕéð öñ ţĥé šéŕṽéŕ, ûšéð öñļý ƒöŕ ýöûŕ ŕéǫûéšţš åñð ñéṽéŕ šĥöŵñ åĝåîñ. one two three four five six seven eight nine ten one]';
@override
- String get emojiNone => '[Ñö éɱöĵî one]';
+ String get aiProvidersProvider => '[Þŕöṽîðéŕ one]';
@override
- String get emojiSearchResults => '[Šéåŕçĥ ŕéšûļţš one t]';
+ String get aiProvidersKey => '[ÅÞÎ ķéý one]';
@override
- String get emojiPickAnEmoji => '[Þîçķ åñ éɱöĵî one]';
+ String get aiProvidersKeyPlaceholder => '[Þåšţé ýöûŕ ÅÞÎ ķéý one tw]';
@override
- String emojiSkinToneOption(String tone) {
- return '[$tone šķîñ ţöñé one]';
- }
+ String get aiProvidersSave => '[Šåṽé ķéý one]';
@override
- String emojiSkinToneCurrent(String tone) {
- return '[Šķîñ ţöñé: $tone one]';
- }
+ String get aiProvidersSaved => '[ÅÞÎ ķéý šåṽéð one]';
@override
- String get emojiSkinToneDefault => '[Ðéƒåûļţ one]';
+ String get aiProvidersSaveFailed => '[Çöûļðñ\'ţ šåṽé ţĥé ÅÞÎ ķéý one two]';
@override
- String get emojiSkinToneLight => '[Ļîĝĥţ on]';
+ String get aiProvidersCurrent => '[Ýöûŕ ķéý one]';
@override
- String get emojiSkinToneMediumLight => '[Ṁéðîûɱ-ļîĝĥţ one t]';
+ String aiProvidersCurrentValue(String provider, String last4) {
+ return '[$provider ķéý éñðîñĝ îñ $last4 one]';
+ }
@override
- String get emojiSkinToneMedium => '[Ṁéðîûɱ one]';
+ String get aiProvidersRemove => '[Ŕéɱöṽé ķéý one]';
@override
- String get emojiSkinToneMediumDark => '[Ṁéðîûɱ-ðåŕķ one]';
+ String get aiProvidersRemoveTitle => '[Ŕéɱöṽé ýöûŕ ÅÞÎ ķéý? one tw]';
@override
- String get emojiSkinToneDark => '[Ðåŕķ on]';
+ String get aiProvidersRemoveBody =>
+ '[ÅÎ ƒéåţûŕéš šţöþ ŵöŕķîñĝ ƒöŕ ýöû ûñţîļ ýöû åðð å ķéý åĝåîñ. one two three fou]';
@override
- String get specialCharsTitle => '[Šþéçîåļ çĥåŕåçţéŕš one tw]';
+ String get aiProvidersRemoved => '[ÅÞÎ ķéý ŕéɱöṽéð one t]';
@override
- String get specialCharsClose => '[Çļöšé on]';
+ String get aiProvidersRemoveFailed =>
+ '[Çöûļðñ\'ţ ŕéɱöṽé ţĥé ÅÞÎ ķéý one two t]';
@override
- String get specialCharsSearch =>
- '[Šéåŕçĥ ƀý ñåɱé öŕ çöðé (Û+2192) one two th]';
+ String get aiProvidersNotAllowed =>
+ '[Ţĥîš šéŕṽéŕ ðöéšñ\'ţ åļļöŵ þéŕšöñåļ ÅÞÎ ķéýš. Åšķ åñ åðɱîñîšţŕåţöŕ ţö šéţ ûþ ÅÎ. one two three four five]';
@override
- String get specialCharsRecent => '[Ŕéçéñţ one]';
+ String get aiProvidersManage => '[Ṁåñåĝé ÅÎ þŕöṽîðéŕš one tw]';
@override
- String get specialCharsLoadFailed =>
- '[Çĥåŕåçţéŕš çöûļð ñöţ ƀé ļöåðéð one two th]';
+ String get aiChatHistory => '[Çĥåţ ĥîšţöŕý one]';
@override
- String get specialCharsNoneFound => '[Ñö çĥåŕåçţéŕš ƒöûñð one tw]';
+ String get aiChatHistoryHint =>
+ '[Ðéļéţé åļļ ýöûŕ åššîšţåñţ çĥåţš. one two th]';
@override
- String get specialCharsSearchResults => '[Šéåŕçĥ ŕéšûļţš one t]';
+ String get aiClearHistory => '[Çļéåŕ ĥîšţöŕý one t]';
@override
- String activityDetailSlides(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '$count šļîðéš',
- one: '1 šļîðé',
- );
- return '[$_temp0 one t]';
- }
+ String get aiClearHistoryTitle => '[Çļéåŕ åššîšţåñţ ĥîšţöŕý? one two]';
@override
- String activityDetailSlidesDeleted(int count) {
+ String get aiClearHistoryBody =>
+ '[Åļļ ýöûŕ åššîšţåñţ çĥåţš îñ éṽéŕý ðöçûɱéñţ åŕé ðéļéţéð. Ţĥîš çåñ\'ţ ƀé ûñðöñé. Éðîţš ţĥé åššîšţåñţ ɱåðé šţåý îñ ţĥé ðöçûɱéñţš. one two three four five six seven eig]';
+
+ @override
+ String get aiClearHistoryFailed => '[Çöûļðñ\'ţ çļéåŕ ţĥé ĥîšţöŕý one two t]';
+
+ @override
+ String aiHistoryCleared(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
- other: '$count šļîðéš ðéļéţéð',
- one: '1 šļîðé ðéļéţéð',
+ other: 'Ðéļéţéð $count çĥåţš',
+ one: 'Ðéļéţéð 1 çĥåţ',
);
- return '[$_temp0 one two th]';
+ return '[$_temp0 one two t]';
}
@override
- String get activityDetailTheme => '[ţĥéɱé çĥåñĝéð one t]';
+ String get mcpScopeDriveRead =>
+ '[Ðŕîṽé: ŕéåð (ļîšţ, šéåŕçĥ, éẋþöŕţ, ðöŵñļöåð ƒîļéš, šţöŕåĝé) one two three four]';
@override
- String get activityDetailAgent => '[ŵîţĥ Çļåûðé one]';
+ String get mcpScopeDriveWrite =>
+ '[Ðŕîṽé: ŵŕîţé (çŕéåţé, ŕéñåɱé, ɱöṽé, ţŕåšĥ, šĥåŕé, ûþļöåð åñð çöñṽéŕţ ƒîļéš) one two three four five]';
@override
- String get markdownBold => '[Ɓöļð on]';
+ String get mcpScopeWriteRead => '[Ðöçûɱéñţš: ŕéåð one t]';
@override
- String get markdownItalic => '[Îţåļîç one]';
+ String get mcpScopeWriteEdit =>
+ '[Ðöçûɱéñţš: éðîţ (çöñţéñţ, çöɱɱéñţš, šûĝĝéšţîöñš) one two three fo]';
@override
- String get markdownCode => '[Çöðé on]';
+ String get mcpScopeSheetsRead => '[Šþŕéåðšĥééţš: ŕéåð one tw]';
@override
- String get markdownLink => '[Ļîñķ on]';
+ String get mcpScopeSheetsEdit => '[Šþŕéåðšĥééţš: éðîţ one tw]';
@override
- String get markdownBulletedList => '[Ɓûļļéţéð ļîšţ one t]';
+ String get mcpScopeSlidesRead => '[Þŕéšéñţåţîöñš: ŕéåð one two]';
@override
- String get markdownStrikethrough => '[Šţŕîķéţĥŕöûĝĥ one t]';
+ String get mcpScopeSlidesEdit => '[Þŕéšéñţåţîöñš: éðîţ one two]';
@override
- String get markdownNumberedList => '[Ñûɱƀéŕéð ļîšţ one t]';
+ String get mcpScopeScriptsRun => '[Šçŕîþţš: ŕûñ one]';
@override
- String get markdownTaskList => '[Çĥéçķļîšţ one]';
+ String get mcpScopeUiControl =>
+ '[Çöñţŕöļ åþþ ŵîñðöŵš (ûî.* ţööļš) one two th]';
@override
- String get markdownQuote => '[Ǫûöţé on]';
+ String get mcpScopeBoard =>
+ '[Ɓûĝš & îɱþŕöṽéɱéñţš: ŕéåð, ŕéþöŕţ îššûéš, ṽöţé, çļåîɱ ţåšķš one two three four]';
@override
- String get markdownToolbar => '[Ƒöŕɱåţţîñĝ one]';
+ String get tokenCreateHint =>
+ '[Ţĥé ţöķéñ åçţš åš ýöû ŵîţĥ ţĥé þéŕɱîššîöñš ýöû çĥööšé. Ýöû çåñ ŕéṽöķé îţ åţ åñý ţîɱé. one two three four five s]';
@override
- String get markdownLinkTitle => '[Îñšéŕţ ļîñķ one]';
+ String get tokenNameRequired => '[Ĝîṽé ţĥé ţöķéñ å ñåɱé. one two]';
@override
- String get markdownLinkEditTitle => '[Éðîţ ļîñķ one]';
+ String get tokenScopesRequired =>
+ '[Çĥööšé åţ ļéåšţ öñé þéŕɱîššîöñ. one two th]';
@override
- String get markdownLinkRemove => '[Ŕéɱöṽé ļîñķ one]';
+ String get tokenExpires => '[Éẋþîŕéš one]';
@override
- String get markdownLinkUrl => '[ÛŔĻ on]';
+ String tokenExpiresInDays(int days) {
+ String _temp0 = intl.Intl.pluralLogic(
+ days,
+ locale: localeName,
+ other: 'Îñ $days ðåýš',
+ one: 'Îñ 1 ðåý',
+ );
+ return '[$_temp0 one t]';
+ }
@override
- String get markdownLinkInsert => '[Îñšéŕţ one]';
+ String get tokenExpiresInYear => '[Îñ 1 ýéåŕ one]';
@override
- String get markdownLinkInvalid =>
- '[Éñţéŕ å ŵéƀ (ĥţţþ öŕ ĥţţþš) öŕ éɱåîļ (ɱåîļţö) ļîñķ one two three f]';
+ String get tokenNever => '[Ñéṽéŕ on]';
@override
- String markdownLength(int count, int max) {
- return '[$count / $max o]';
- }
+ String get tokenNeverLower => '[ñéṽéŕ on]';
@override
- String get commentAddReaction => '[Åðð ŕéåçţîöñ one]';
+ String get tokenExpired => '[éẋþîŕéð one]';
@override
- String commentReaction(String emoji, int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '$count ŕéåçţîöñš',
- one: '1 ŕéåçţîöñ',
- );
- return '[$emoji $_temp0 one two]';
+ String tokenNameWithStatus(String name, String status) {
+ return '[$name · $status o]';
}
@override
- String get lightboxImage => '[Îɱåĝé on]';
+ String tokenDates(String created, String expires, String lastUsed) {
+ return '[Çŕéåţéð $created · éẋþîŕéš $expires · ļåšţ ûšéð $lastUsed one two t]';
+ }
@override
- String get lightboxClose => '[Çļöšé (Éšç) one]';
+ String tokenRevokeTitle(String name) {
+ return '[Ŕéṽöķé \"$name\"? one]';
+ }
@override
- String get lightboxCloseImage => '[Çļöšé îɱåĝé one]';
+ String get tokenRevokeBody =>
+ '[ṀÇÞ çļîéñţš ûšîñĝ ţĥîš ţöķéñ šţöþ ŵöŕķîñĝ îɱɱéðîåţéļý. Ţĥîš çåñ\'ţ ƀé ûñðöñé. one two three four five]';
@override
- String get lightboxLoadFailed => '[Ţĥé îɱåĝé çöûļðñ’ţ ƀé ļöåðéð one two t]';
+ String get toastTokenCopied => '[Ţöķéñ çöþîéð one]';
@override
- String get lightboxZoomIn => '[Žööɱ îñ one]';
+ String mcpEndpoint(String url) {
+ return '[Éñðþöîñţ: $url one]';
+ }
@override
- String get lightboxZoomOut => '[Žööɱ öûţ one]';
+ String mcpReplaceToken(String variable) {
+ return '[Ŕéþļåçé $variable ŵîţĥ å ţöķéñ. one two]';
+ }
@override
- String get lightboxFit => '[Ƒîţ ţö šçŕééñ one]';
+ String get mcpWindowControl => '[Ŵîñðöŵ çöñţŕöļ one t]';
@override
- String get cmdBoardReport => '[Ɓûĝš & îɱþŕöṽéɱéñţš… one two]';
+ String get mcpWindowControlHint =>
+ '[Ļéţš ṀÇÞ çļîéñţš ŵîţĥ ţĥé ûî:çöñţŕöļ þéŕɱîššîöñ ðŕîṽé ţĥîš ŵîñðöŵ: ñåṽîĝåţé, ŕûñ çöɱɱåñðš, ƒîļļ ðîåļöĝš, ţåķé šçŕééñšĥöţš. Å þîļļ ŵîţĥ Šţöþ šĥöŵš ŵĥîļé åñ åĝéñţ ŵöŕķš. one two three four five six seven eight nine ten o]';
@override
- String get cmdBoardOpen => '[Ɓŕöŵšé Ɓûĝš & îɱþŕöṽéɱéñţš one two t]';
+ String get mcpBridgeStatus => '[Šţåţûš one]';
@override
- String get boardReportName => '[Ɓûĝš & îɱþŕöṽéɱéñţš one tw]';
+ String get mcpBridgeOff => '[Öƒƒ on]';
@override
- String get boardContributeNav => '[Çöñţŕîƀûţé & ƀûîļð one tw]';
+ String get mcpBridgeConnecting => '[Çöññéçţîñĝ… one]';
@override
- String get boardReportKindLabel => '[Ķîñð on]';
-
- @override
- String get boardReportKindBug => '[Ŕéþöŕţ å ƀûĝ one]';
+ String mcpBridgeConnected(String id) {
+ return '[Öñ · ŵîñðöŵ $id one]';
+ }
@override
- String get boardReportKindIdea => '[Šûĝĝéšţ åñ îɱþŕöṽéɱéñţ one two]';
+ String driveItemLabel(String kind, String name) {
+ return '[$kind $name]';
+ }
@override
- String get boardReportFieldTitle => '[Ţîţļé on]';
+ String get activityShowMore => '[Šĥöŵ ɱöŕé one]';
@override
- String get boardReportTitleHintBug =>
- '[Ðéšçŕîƀé ţĥé þŕöƀļéɱ îñ å ƒéŵ ŵöŕðš one two thr]';
+ String get notificationsFilterAll => '[Åļļ on]';
@override
- String get boardReportTitleHintIdea =>
- '[Ðéšçŕîƀé ýöûŕ îðéå îñ å ƒéŵ ŵöŕðš one two th]';
+ String get notificationsFilterUnread => '[Ûñŕéåð one]';
@override
- String get boardReportFieldHappened => '[Ŵĥåţ ĥåþþéñéð? one t]';
+ String notificationsFilterUnreadCount(int count) {
+ return '[Ûñŕéåð ($count) one]';
+ }
@override
- String get boardReportFieldExpected => '[Ŵĥåţ ðîð ýöû éẋþéçţ? one tw]';
+ String get notificationsSettings => '[Ñöţîƒîçåţîöñ šéţţîñĝš one two]';
@override
- String get boardReportFieldSteps => '[Šţéþš ţö ŕéþŕöðûçé one tw]';
+ String get notificationsLoadFailed =>
+ '[Çöûļðñ\'ţ ļöåð ýöûŕ ñöţîƒîçåţîöñš one two thr]';
@override
- String get boardReportFieldIdea => '[Ŵĥåţ ŵöûļð ýöû ļîķé? one tw]';
+ String get notificationsEmptyUnread => '[Ñö ûñŕéåð ñöţîƒîçåţîöñš one two]';
@override
- String get boardReportFieldWhy => '[Ŵĥý ŵöûļð îţ ĥéļþ? one tw]';
+ String get notificationsLoadMore => '[Ļöåð ɱöŕé one]';
@override
- String get boardReportOptional => '[(öþţîöñåļ) one]';
+ String get notificationsUnreadMarker => '[ûñŕéåð one]';
@override
- String boardReportTitleLength(int min, int max) {
- return '[Ûšé $min ţö $max çĥåŕåçţéŕš. one tw]';
+ String notifSuggestionAccepted(String who, String item) {
+ return '[$who åççéþţéð ýöûŕ šûĝĝéšţîöñ îñ $item one two t]';
}
@override
- String boardReportBodyTooLong(int max) {
- return '[Ţĥé ðéšçŕîþţîöñ îš ţöö ļöñĝ: åţ ɱöšţ $max çĥåŕåçţéŕš. one two three f]';
+ String notifSuggestionRejected(String who, String item) {
+ return '[$who ŕéĵéçţéð ýöûŕ šûĝĝéšţîöñ îñ $item one two t]';
}
@override
- String get boardReportSimilarTitle => '[Šîɱîļåŕ îššûéš one t]';
-
- @override
- String boardReportSimilarVotes(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '$count ṽöţéš',
- one: '1 ṽöţé',
- );
- return '[$_temp0 one]';
- }
+ String get notifyAccessRequests => '[Åççéšš ŕéǫûéšţš one t]';
@override
- String boardSimilarShow(int number, String title) {
- return '[Šĥöŵ #$number $title on]';
- }
+ String get notifySuggestions => '[Šûĝĝéšţîöñš öñ ɱý ƒîļéš one two]';
@override
- String get boardSimilarLoadFailed =>
- '[Çöûļðñ\'ţ ļöåð ţĥîš îššûé. Ţŕý åĝåîñ ļåţéŕ. one two three]';
+ String get notifyDigest => '[Ɓûñðļé éɱåîļš one t]';
@override
- String get boardTaskCommentsLabel => '[Çöɱɱéñţš ƒöŕ ţĥé þŕöɱþţ one two]';
+ String get notifyDigestHint =>
+ '[Šéñð öñé éɱåîļ éṽéŕý 15 ɱîñûţéš îñšţéåð öƒ öñé þéŕ ñöţîƒîçåţîöñ. Ṁéñţîöñš åñð åššîĝñɱéñţš åŕé åļŵåýš éɱåîļéð ŕîĝĥţ åŵåý. one two three four five six seven ei]';
@override
- String get boardTaskCommentsHint =>
- '[Ţĥé ÅÎ ŕéåðš ţĥé çĥéçķéð çöɱɱéñţš ŵĥéñ îţ ŵŕîţéš ţĥé þŕöɱþţ. one two three four]';
+ String get exportFormatMarkdown => '[Ṁåŕķðöŵñ (.ɱð) one t]';
@override
- String get boardCommentsPromptHint =>
- '[Çĥéçķéð çöɱɱéñţš ĝö îñţö ţĥé ţåšķ þŕöɱþţ ŵĥéñ ýöû šéļéçţ ţĥîš îššûé ƒöŕ ƀûîļðîñĝ. one two three four five]';
+ String get exportFormatHtml => '[ĤŢṀĻ (.ĥţɱļ) one]';
@override
- String boardCommentInPrompt(String name) {
- return '[Îñçļûðé ţĥé çöɱɱéñţ ƀý $name îñ ţĥé ţåšķ þŕöɱþţ one two thre]';
- }
+ String get exportFormatPdf => '[ÞÐƑ (.þðƒ) one]';
@override
- String get boardReportVoteInstead => '[Ṽöţé îñšţéåð one]';
+ String get exportFormatText => '[Þļåîñ ţéẋţ (.ţẋţ) one tw]';
@override
- String get boardReportVoted => '[Ṽöţéð on]';
+ String get badgePendingCreate =>
+ '[Çŕéåţéð öƒƒļîñé. Îţ îš åððéð ţö ýöûŕ Ðŕîṽé ŵĥéñ ýöû\'ŕé ƀåçķ öñļîñé. one two three four f]';
@override
- String boardReportVotedFor(int number) {
- return '[Ṽöţéð ƒöŕ #$number one]';
+ String dropToUploadInto(String folder) {
+ return '[Ðŕöþ ƒîļéš ţö ûþļöåð ţö $folder one two]';
}
@override
- String boardReportHitThisToo(int number) {
- return '[Åððéð “Î ĥîţ ţĥîš ţöö” ţö #$number one two]';
+ String dropUploadToFolder(String folder) {
+ return '[Ûþļöåð ţö $folder one]';
}
@override
- String boardReportVoteFailed(String message) {
- return '[Çöûļðñ’ţ ṽöţé: $message one t]';
- }
+ String get driveItemUploading => '[Ûþļöåðîñĝ… one]';
@override
- String get boardReportScreenshot => '[Šçŕééñšĥöţ one]';
+ String get spellingTitle => '[Šþéļļîñĝ one]';
@override
- String get boardReportAddScreenshot => '[Åðð šçŕééñšĥöţ one t]';
+ String get spellingDone => '[Ðöñé on]';
@override
- String get boardReportEditScreenshot => '[Éðîţ on]';
+ String get spellingNoErrors => '[Ñö šþéļļîñĝ éŕŕöŕš ƒöûñð. one two]';
@override
- String get boardReportRemoveScreenshot => '[Ŕéɱöṽé one]';
+ String get spellingUnavailable =>
+ '[Šþéļļçĥéçķ îšñ’ţ åṽåîļåƀļé ƒöŕ ţĥîš ðöçûɱéñţ’š ļåñĝûåĝé. one two three four]';
@override
- String get boardReportScreenshotPreview => '[Šçŕééñšĥöţ þŕéṽîéŵ one tw]';
+ String get spellingNotInDictionary => '[Ñöţ îñ ðîçţîöñåŕý one tw]';
@override
- String get boardReportScreenshotWarning =>
- '[Šçŕééñšĥöţš åŕé þûƀļîç. Çöṽéŕ ñåɱéš, éɱåîļš åñð ðöçûɱéñţ çöñţéñţ. one two three four f]';
+ String get spellingSuggestions => '[Šûĝĝéšţîöñš one]';
@override
- String get boardReportCaptureFailed =>
- '[Çöûļðñ’ţ çåþţûŕé ţĥé šçŕééñ one two t]';
+ String get spellingNoSuggestions => '[Ñö šûĝĝéšţîöñš one t]';
@override
- String boardReportDetails(String summary) {
- return '[Ðéţåîļš ţĥåţ ŵîļļ ƀé îñçļûðéð: $summary one two th]';
- }
+ String get spellingChange => '[Çĥåñĝé one]';
@override
- String get boardReportDetailApp => '[Åþþ on]';
+ String get spellingChangeAll => '[Çĥåñĝé åļļ one]';
@override
- String get boardReportDetailPlatform => '[Þļåţƒöŕɱ one]';
+ String get spellingIgnore => '[Îĝñöŕé one]';
@override
- String get boardReportDetailTheme => '[Ţĥéɱé on]';
+ String get spellingIgnoreAll => '[Îĝñöŕé åļļ one]';
@override
- String get boardReportDetailLocale => '[Ļåñĝûåĝé one]';
+ String get spellingAddToDictionary => '[Åðð ţö ðîçţîöñåŕý one tw]';
@override
- String get boardReportDetailWindow => '[Ŵîñðöŵ šîžé one]';
+ String get spellingPersonalDictionary => '[Þéŕšöñåļ ðîçţîöñåŕý one two]';
@override
- String get boardReportDetailRoute => '[Þåĝé on]';
+ String get spellingPersonalDictionaryHint =>
+ '[Ŵöŕðš ýöû åððéð åŕé ñéṽéŕ ɱåŕķéð åš ɱîššþéļļéð, îñ åñý ðöçûɱéñţ. one two three four]';
@override
- String get boardReportDetailError => '[Éŕŕöŕ on]';
+ String get spellingAddWord => '[Åðð å ŵöŕð one]';
@override
- String get boardReportThemeLight => '[ļîĝĥţ ţĥéɱé one]';
+ String get spellingAdd => '[Åðð on]';
@override
- String get boardReportThemeDark => '[ðåŕķ ţĥéɱé one]';
+ String get spellingNoWords => '[Ñö ŵöŕðš ýéţ. one]';
@override
- String boardReportIncludeError(String age) {
- return '[Îñçļûðé ţĥé ļåšţ éŕŕöŕ ($age) one two]';
+ String spellingRemoveWord(String word) {
+ return '[Ŕéɱöṽé $word one]';
}
@override
- String boardReportErrorAge(int minutes) {
- String _temp0 = intl.Intl.pluralLogic(
- minutes,
- locale: localeName,
- other: '$minutes ɱîñ åĝö',
- one: '1 ɱîñ åĝö',
- zero: 'ĵûšţ ñöŵ',
- );
- return '[$_temp0 one two]';
- }
+ String get emojiPickerClose => '[Çļöšé éɱöĵî þîçķéŕ one tw]';
@override
- String boardReportErrorAgeHours(int hours) {
- String _temp0 = intl.Intl.pluralLogic(
- hours,
- locale: localeName,
- other: '$hours ĥ åĝö',
- one: '1 ĥ åĝö',
- );
- return '[$_temp0 one]';
- }
+ String get emojiDialogLabel => '[Éɱöĵî on]';
@override
- String get boardReportAiNotice =>
- '[Κšûéš åŕé þûƀļîç åñð çĥéçķéð åûţöɱåţîçåļļý ƀý ÅÎ (Ĵéṽ, Ĝéɱîñî). one two three four f]';
+ String get emojiPickerLabel => '[Éɱöĵî þîçķéŕ one]';
@override
- String get boardReportPublicNotice => '[Κšûéš åŕé þûƀļîç. one tw]';
+ String get emojiSearch => '[Šéåŕçĥ éɱöĵî one]';
@override
- String boardReportSecurityNotice(String contact) {
- return '[Šéçûŕîţý îššûé? Ðöñ’ţ þöšţ îţ ĥéŕé: éɱåîļ $contact one two three]';
- }
+ String get emojiRecentlyUsed => '[Ŕéçéñţļý ûšéð one t]';
@override
- String get boardReportPostBug => '[Þöšţ ƀûĝ one]';
+ String get emojiLoadFailed => '[Éɱöĵî çöûļð ñöţ ƀé ļöåðéð one two]';
@override
- String get boardReportPostIdea => '[Þöšţ îɱþŕöṽéɱéñţ one tw]';
+ String get emojiNoneFound => '[Ñö éɱöĵî ƒöûñð one t]';
@override
- String get boardReportPosted => '[Þöšţéð one]';
+ String get emojiNone => '[Ñö éɱöĵî one]';
@override
- String get boardReportPostedChecking => '[Þöšţéð. Çĥéçķîñĝ îţ ñöŵ… one two]';
+ String get emojiSearchResults => '[Šéåŕçĥ ŕéšûļţš one t]';
@override
- String get boardReportPostedHeld =>
- '[Þöšţéð. Ŵåîţîñĝ ƒöŕ ŕéṽîéŵ: å ɱöðéŕåţöŕ ŵîļļ ļööķ åţ îţ. one two three fou]';
+ String get emojiPickAnEmoji => '[Þîçķ åñ éɱöĵî one]';
@override
- String get boardReportView => '[Ṽîéŵ on]';
+ String emojiSkinToneOption(String tone) {
+ return '[$tone šķîñ ţöñé one]';
+ }
@override
- String get boardReportRateLimited =>
- '[Ýöû’ṽé þöšţéð å ļöţ ŕéçéñţļý. Þļéåšé ţŕý åĝåîñ ļåţéŕ. one two three fo]';
+ String emojiSkinToneCurrent(String tone) {
+ return '[Šķîñ ţöñé: $tone one]';
+ }
@override
- String get boardReportBlocked =>
- '[Ýöû çåñ’ţ þöšţ öñ Ɓûĝš & îɱþŕöṽéɱéñţš. one two thre]';
+ String get emojiSkinToneDefault => '[Ðéƒåûļţ one]';
@override
- String boardReportBlockedReason(String reason) {
- return '[Ýöû çåñ’ţ þöšţ öñ Ɓûĝš & îɱþŕöṽéɱéñţš: $reason one two thre]';
- }
+ String get emojiSkinToneLight => '[Ļîĝĥţ on]';
@override
- String boardReportPostFailed(String message) {
- return '[Çöûļðñ’ţ þöšţ: $message one t]';
- }
+ String get emojiSkinToneMediumLight => '[Ṁéðîûɱ-ļîĝĥţ one t]';
@override
- String boardReportUploadFailed(String message) {
- return '[Çöûļðñ’ţ ûþļöåð ţĥé šçŕééñšĥöţ: $message one two th]';
- }
+ String get emojiSkinToneMedium => '[Ṁéðîûɱ one]';
@override
- String get boardReportDiscardTitle => '[Ðîšçåŕð ðŕåƒţ? one t]';
+ String get emojiSkinToneMediumDark => '[Ṁéðîûɱ-ðåŕķ one]';
@override
- String get boardReportDiscardBody =>
- '[Ýöûŕ îššûé ĥåšñ’ţ ƀééñ þöšţéð ýéţ. one two thr]';
+ String get emojiSkinToneDark => '[Ðåŕķ on]';
@override
- String get boardReportDiscard => '[Ðîšçåŕð one]';
+ String get specialCharsTitle => '[Šþéçîåļ çĥåŕåçţéŕš one tw]';
@override
- String get boardReportKeepDraft => '[Ķééþ ðŕåƒţ one]';
+ String get specialCharsClose => '[Çļöšé on]';
@override
- String get errorReportBug => '[Ŕéþöŕţ ţĥîš ƀûĝ one t]';
+ String get specialCharsSearch =>
+ '[Šéåŕçĥ ƀý ñåɱé öŕ çöðé (Û+2192) one two th]';
@override
- String get screenshotEditorTitle => '[Éðîţ šçŕééñšĥöţ one t]';
+ String get specialCharsRecent => '[Ŕéçéñţ one]';
@override
- String get screenshotEditorWarning =>
- '[Ţĥîš šçŕééñšĥöţ ŵîļļ ƀé þûƀļîç. Çöṽéŕ åñýţĥîñĝ þŕîṽåţé: ñåɱéš, éɱåîļš, ðöçûɱéñţ åñð šþŕéåðšĥééţ çöñţéñţ ýöû ðöñ’ţ ŵåñţ ţö šĥåŕé. one two three four five six seven eight]';
+ String get specialCharsLoadFailed =>
+ '[Çĥåŕåçţéŕš çöûļð ñöţ ƀé ļöåðéð one two th]';
@override
- String get screenshotToolsLabel => '[Ţööļ on]';
+ String get specialCharsNoneFound => '[Ñö çĥåŕåçţéŕš ƒöûñð one tw]';
@override
- String get screenshotColorsLabel => '[Çöļöŕ on]';
+ String get specialCharsSearchResults => '[Šéåŕçĥ ŕéšûļţš one t]';
@override
- String get screenshotToolCover => '[Çöṽéŕ on]';
+ String activityDetailSlides(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count šļîðéš',
+ one: '1 šļîðé',
+ );
+ return '[$_temp0 one t]';
+ }
@override
- String get screenshotToolHighlight => '[Ĥîĝĥļîĝĥţ one]';
+ String activityDetailSlidesDeleted(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count šļîðéš ðéļéţéð',
+ one: '1 šļîðé ðéļéţéð',
+ );
+ return '[$_temp0 one two th]';
+ }
@override
- String get screenshotToolArrow => '[Åŕŕöŵ on]';
+ String get activityDetailTheme => '[ţĥéɱé çĥåñĝéð one t]';
@override
- String get screenshotToolPen => '[Þéñ on]';
+ String get activityDetailAgent => '[ŵîţĥ Çļåûðé one]';
@override
- String get screenshotColorRed => '[Ŕéð on]';
+ String get markdownBold => '[Ɓöļð on]';
@override
- String get screenshotColorYellow => '[Ýéļļöŵ one]';
+ String get markdownItalic => '[Îţåļîç one]';
@override
- String get screenshotColorBlue => '[Ɓļûé on]';
+ String get markdownCode => '[Çöðé on]';
@override
- String get screenshotUndo => '[Ûñðö on]';
+ String get markdownLink => '[Ļîñķ on]';
@override
- String get screenshotRedo => '[Ŕéðö on]';
+ String get markdownBulletedList => '[Ɓûļļéţéð ļîšţ one t]';
@override
- String get screenshotDelete => '[Ðéļéţé šéļéçţéð one t]';
+ String get markdownStrikethrough => '[Šţŕîķéţĥŕöûĝĥ one t]';
@override
- String get screenshotClear => '[Çļéåŕ on]';
+ String get markdownNumberedList => '[Ñûɱƀéŕéð ļîšţ one t]';
@override
- String get screenshotDone => '[Ðöñé on]';
+ String get markdownTaskList => '[Çĥéçķļîšţ one]';
@override
- String screenshotShortcut(String label, String shortcut) {
- return '[$label ($shortcut) o]';
- }
+ String get markdownQuote => '[Ǫûöţé on]';
@override
- String get screenshotCanvasLabel =>
- '[Šçŕééñšĥöţ. Ðŕåĝ ţö ðŕåŵ ŵîţĥ ţĥé šéļéçţéð ţööļ; çļîçķ åñ åññöţåţîöñ ţö šéļéçţ îţ. one two three four five s]';
+ String get markdownToolbar => '[Ƒöŕɱåţţîñĝ one]';
@override
- String screenshotEncodeFailed(String message) {
- return '[Çöûļðñ’ţ þŕéþåŕé ţĥé šçŕééñšĥöţ: $message one two thr]';
- }
+ String get markdownLinkTitle => '[Îñšéŕţ ļîñķ one]';
@override
- String get boardTitle => '[Çöñţŕîƀûţé & ƀûîļð one tw]';
+ String get markdownLinkEditTitle => '[Éðîţ ļîñķ one]';
@override
- String get boardBreadcrumb => '[Ɓŕéåðçŕûɱƀ one]';
+ String get markdownLinkRemove => '[Ŕéɱöṽé ļîñķ one]';
@override
- String get boardClose => '[Çļöšé Çöñţŕîƀûţé & ƀûîļð one two]';
+ String get markdownLinkUrl => '[ÛŔĻ on]';
@override
- String get boardShowPath => '[Šĥöŵ ţĥé ƒûļļ þåţĥ one tw]';
+ String get markdownLinkInsert => '[Îñšéŕţ one]';
@override
- String boardTabWithCount(String name, int count) {
- return '[$name, $count o]';
+ String get markdownLinkInvalid =>
+ '[Éñţéŕ å ŵéƀ (ĥţţþ öŕ ĥţţþš) öŕ éɱåîļ (ɱåîļţö) ļîñķ one two three f]';
+
+ @override
+ String markdownLength(int count, int max) {
+ return '[$count / $max o]';
}
@override
- String get boardReportBug => '[Ŕéþöŕţ å ƀûĝ one]';
+ String get commentAddReaction => '[Åðð ŕéåçţîöñ one]';
@override
- String get boardSuggestImprovement => '[Šûĝĝéšţ åñ îɱþŕöṽéɱéñţ one two]';
+ String commentReaction(String emoji, int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count ŕéåçţîöñš',
+ one: '1 ŕéåçţîöñ',
+ );
+ return '[$emoji $_temp0 one two]';
+ }
@override
- String get boardSearch => '[Šéåŕçĥ îššûéš one t]';
+ String get lightboxImage => '[Îɱåĝé on]';
@override
- String get boardSignIn => '[Šîĝñ îñ one]';
+ String get lightboxClose => '[Çļöšé (Éšç) one]';
@override
- String get boardSignInToVote =>
- '[Šîĝñ îñ ţö ṽöţé åñð ŕéþöŕţ îššûéš. one two th]';
+ String get lightboxCloseImage => '[Çļöšé îɱåĝé one]';
@override
- String get boardSignInToComment => '[Šîĝñ îñ ţö çöɱɱéñţ one tw]';
+ String get lightboxLoadFailed => '[Ţĥé îɱåĝé çöûļðñ’ţ ƀé ļöåðéð one two t]';
@override
- String get boardSignInToClaim => '[Šîĝñ îñ ţö çļåîɱ ţĥîš ţåšķ one two]';
+ String get lightboxZoomIn => '[Žööɱ îñ one]';
@override
- String get boardBlocked =>
- '[Ýöû çåñ\'ţ þöšţ öñ Ɓûĝš & îɱþŕöṽéɱéñţš. one two thre]';
+ String get lightboxZoomOut => '[Žööɱ öûţ one]';
@override
- String get boardContributeLink => '[Ĥöŵ ţö çöñţŕîƀûţé one tw]';
+ String get lightboxFit => '[Ƒîţ ţö šçŕééñ one]';
@override
- String get boardTabImprovements => '[Îɱþŕöṽéɱéñţš one t]';
+ String get cmdBoardReport => '[Ɓûĝš & îɱþŕöṽéɱéñţš… one two]';
@override
- String get boardTabBugs => '[Ɓûĝš on]';
+ String get cmdBoardOpen => '[Ɓŕöŵšé Ɓûĝš & îɱþŕöṽéɱéñţš one two t]';
@override
- String get boardTabTasks => '[Ûþ ƒöŕ ĝŕåƀš one]';
+ String get boardReportName => '[Ɓûĝš & îɱþŕöṽéɱéñţš one tw]';
@override
- String get boardTabClaimed => '[Çļåîɱéð one]';
+ String get boardContributeNav => '[Çöñţŕîƀûţé & ƀûîļð one tw]';
@override
- String get boardSearchButton => '[Šéåŕçĥ one]';
+ String get boardReportKindLabel => '[Ķîñð on]';
@override
- String boardSearchActive(String query) {
- return '[Šéåŕçĥ: “$query” one]';
- }
+ String get boardReportKindBug => '[Ŕéþöŕţ å ƀûĝ one]';
@override
- String get boardSearchRemove => '[Çļéåŕ ţĥé šéåŕçĥ one t]';
+ String get boardReportKindIdea => '[Šûĝĝéšţ åñ îɱþŕöṽéɱéñţ one two]';
@override
- String get boardSearchIncludeClosed => '[Îñçļûðé çļöšéð one t]';
+ String get boardReportFieldTitle => '[Ţîţļé on]';
@override
- String get boardSearchIncludeClosedHint =>
- '[Ðöñé, šĥîþþéð, ðéçļîñéð åñð ðûþļîçåţéš one two thre]';
+ String get boardReportTitleHintBug =>
+ '[Ðéšçŕîƀé ţĥé þŕöƀļéɱ îñ å ƒéŵ ŵöŕðš one two thr]';
@override
- String get boardSearchKind => '[Ķîñð on]';
+ String get boardReportTitleHintIdea =>
+ '[Ðéšçŕîƀé ýöûŕ îðéå îñ å ƒéŵ ŵöŕðš one two th]';
@override
- String get boardSearchKindAll => '[Åļļ on]';
+ String get boardReportFieldHappened => '[Ŵĥåţ ĥåþþéñéð? one t]';
@override
- String get boardSearchResults => '[Šéåŕçĥ one]';
+ String get boardReportFieldExpected => '[Ŵĥåţ ðîð ýöû éẋþéçţ? one tw]';
@override
- String boardSearchResultsFor(String query) {
- return '[Ŕéšûļţš ƒöŕ “$query” one t]';
- }
+ String get boardReportFieldSteps => '[Šţéþš ţö ŕéþŕöðûçé one tw]';
@override
- String get boardSearchStartTitle => '[Šéåŕçĥ ţĥé ƀöåŕð one t]';
+ String get boardReportFieldIdea => '[Ŵĥåţ ŵöûļð ýöû ļîķé? one tw]';
@override
- String get boardSearchStartHint =>
- '[Ƒîñð îššûéš ƀý ţĥéîŕ ŵöŕðš, þåŕţ öƒ å ţîţļé öŕ ţĥéîŕ ñûɱƀéŕ. one two three four]';
+ String get boardReportFieldWhy => '[Ŵĥý ŵöûļð îţ ĥéļþ? one tw]';
@override
- String get boardEmptySearchHint =>
- '[Ţŕý ƒéŵéŕ ŵöŕðš öŕ þåŕţ öƒ å ŵöŕð, öŕ îñçļûðé çļöšéð îššûéš. one two three four]';
+ String get boardReportOptional => '[(öþţîöñåļ) one]';
@override
- String get boardEmptyClaimed => '[Ñö çļåîɱéð ţåšķš one t]';
+ String boardReportTitleLength(int min, int max) {
+ return '[Ûšé $min ţö $max çĥåŕåçţéŕš. one tw]';
+ }
@override
- String get boardEmptyClaimedHint =>
- '[Ţåšķš šĥöŵ ĥéŕé ŵĥîļé šöɱéöñé ŵöŕķš öñ ţĥéɱ, åñð ŵĥîļé ţĥéîŕ þûļļ ŕéǫûéšţ îš îñ ŕéṽîéŵ. one two three four five si]';
+ String boardReportBodyTooLong(int max) {
+ return '[Ţĥé ðéšçŕîþţîöñ îš ţöö ļöñĝ: åţ ɱöšţ $max çĥåŕåçţéŕš. one two three f]';
+ }
@override
- String get boardTabDone => '[Ðöñé on]';
+ String get boardReportSimilarTitle => '[Šîɱîļåŕ îššûéš one t]';
@override
- String get boardTabReview => '[Ŕéṽîéŵ one]';
+ String boardReportSimilarVotes(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count ṽöţéš',
+ one: '1 ṽöţé',
+ );
+ return '[$_temp0 one]';
+ }
@override
- String get boardKindBug => '[Ɓûĝ on]';
+ String boardSimilarShow(int number, String title) {
+ return '[Šĥöŵ #$number $title on]';
+ }
@override
- String get boardKindIdea => '[Îɱþŕöṽéɱéñţ one]';
+ String get boardSimilarLoadFailed =>
+ '[Çöûļðñ\'ţ ļöåð ţĥîš îššûé. Ţŕý åĝåîñ ļåţéŕ. one two three]';
@override
- String get boardAreaDrive => '[Ðŕîṽé on]';
+ String get boardTaskCommentsLabel => '[Çöɱɱéñţš ƒöŕ ţĥé þŕöɱþţ one two]';
@override
- String get boardAreaWrite => '[Ŵŕîţé on]';
+ String get boardTaskCommentsHint =>
+ '[Ţĥé ÅÎ ŕéåðš ţĥé çĥéçķéð çöɱɱéñţš ŵĥéñ îţ ŵŕîţéš ţĥé þŕöɱþţ. one two three four]';
@override
- String get boardAreaSheets => '[Šĥééţš one]';
+ String get boardCommentsPromptHint =>
+ '[Çĥéçķéð çöɱɱéñţš ĝö îñţö ţĥé ţåšķ þŕöɱþţ ŵĥéñ ýöû šéļéçţ ţĥîš îššûé ƒöŕ ƀûîļðîñĝ. one two three four five]';
@override
- String get boardAreaSlides => '[Šļîðéš one]';
+ String boardCommentInPrompt(String name) {
+ return '[Îñçļûðé ţĥé çöɱɱéñţ ƀý $name îñ ţĥé ţåšķ þŕöɱþţ one two thre]';
+ }
@override
- String get boardAreaSuite => '[Šûîţé on]';
+ String get boardReportVoteInstead => '[Ṽöţé îñšţéåð one]';
@override
- String get boardAreaServer => '[Šéŕṽéŕ one]';
+ String get boardReportVoted => '[Ṽöţéð on]';
@override
- String get boardSizeSmall => '[Šɱåļļ on]';
+ String boardReportVotedFor(int number) {
+ return '[Ṽöţéð ƒöŕ #$number one]';
+ }
@override
- String get boardSizeMedium => '[Ṁéðîûɱ one]';
+ String boardReportHitThisToo(int number) {
+ return '[Åððéð “Î ĥîţ ţĥîš ţöö” ţö #$number one two]';
+ }
@override
- String get boardSizeLarge => '[Ļåŕĝé on]';
+ String boardReportVoteFailed(String message) {
+ return '[Çöûļðñ’ţ ṽöţé: $message one t]';
+ }
@override
- String get boardStatusOpen => '[Öþéñ on]';
+ String get boardReportScreenshot => '[Šçŕééñšĥöţ one]';
@override
- String get boardStatusPlanned => '[Þļåññéð one]';
+ String get boardReportAddScreenshot => '[Åðð šçŕééñšĥöţ one t]';
@override
- String get boardStatusDone => '[Ðöñé on]';
+ String get boardReportEditScreenshot => '[Éðîţ on]';
@override
- String get boardStatusShipped => '[Šĥîþþéð one]';
+ String get boardReportRemoveScreenshot => '[Ŕéɱöṽé one]';
@override
- String get boardStatusDeclined => '[Ðéçļîñéð one]';
+ String get boardReportScreenshotPreview => '[Šçŕééñšĥöţ þŕéṽîéŵ one tw]';
@override
- String get boardStatusDuplicate => '[Ðûþļîçåţé one]';
+ String get boardReportScreenshotWarning =>
+ '[Šçŕééñšĥöţš åŕé þûƀļîç. Çöṽéŕ ñåɱéš, éɱåîļš åñð ðöçûɱéñţ çöñţéñţ. one two three four f]';
@override
- String get boardTaskDraft => '[Ðŕåƒţ on]';
+ String get boardReportCaptureFailed =>
+ '[Çöûļðñ’ţ çåþţûŕé ţĥé šçŕééñ one two t]';
@override
- String get boardTaskPublished => '[Þûƀļîšĥéð one]';
+ String boardReportDetails(String summary) {
+ return '[Ðéţåîļš ţĥåţ ŵîļļ ƀé îñçļûðéð: $summary one two th]';
+ }
@override
- String get boardTaskClaimed => '[Çļåîɱéð one]';
+ String get boardReportDetailApp => '[Åþþ on]';
@override
- String get boardTaskInReview => '[Îñ ŕéṽîéŵ one]';
+ String get boardReportDetailPlatform => '[Þļåţƒöŕɱ one]';
@override
- String get boardTaskCancelled => '[Çåñçéļļéð one]';
+ String get boardReportDetailTheme => '[Ţĥéɱé on]';
@override
- String get boardModerationChecking => '[Çĥéçķîñĝ one]';
+ String get boardReportDetailLocale => '[Ļåñĝûåĝé one]';
@override
- String get boardModerationVisible => '[Ṽîšîƀļé one]';
+ String get boardReportDetailWindow => '[Ŵîñðöŵ šîžé one]';
@override
- String get boardModerationHeld => '[Ĥéļð on]';
+ String get boardReportDetailRoute => '[Þåĝé on]';
@override
- String get boardModerationRemoved => '[Ŕéɱöṽéð one]';
+ String get boardReportDetailError => '[Éŕŕöŕ on]';
@override
- String get boardReasonOffensive => '[Öƒƒéñšîṽé one]';
+ String get boardReportThemeLight => '[ļîĝĥţ ţĥéɱé one]';
@override
- String get boardReasonSpam => '[Šþåɱ on]';
+ String get boardReportThemeDark => '[ðåŕķ ţĥéɱé one]';
@override
- String get boardReasonPersonalData => '[Þéŕšöñåļ ðåţå one t]';
+ String boardReportIncludeError(String age) {
+ return '[Îñçļûðé ţĥé ļåšţ éŕŕöŕ ($age) one two]';
+ }
@override
- String get boardReasonSecurity => '[Šéçûŕîţý îššûé one t]';
+ String boardReportErrorAge(int minutes) {
+ String _temp0 = intl.Intl.pluralLogic(
+ minutes,
+ locale: localeName,
+ other: '$minutes ɱîñ åĝö',
+ one: '1 ɱîñ åĝö',
+ zero: 'ĵûšţ ñöŵ',
+ );
+ return '[$_temp0 one two]';
+ }
@override
- String get boardReasonOther => '[Öţĥéŕ on]';
+ String boardReportErrorAgeHours(int hours) {
+ String _temp0 = intl.Intl.pluralLogic(
+ hours,
+ locale: localeName,
+ other: '$hours ĥ åĝö',
+ one: '1 ĥ åĝö',
+ );
+ return '[$_temp0 one]';
+ }
@override
- String get boardSortTrending => '[Ţŕéñðîñĝ one]';
+ String get boardReportAiNotice =>
+ '[Κšûéš åŕé þûƀļîç åñð çĥéçķéð åûţöɱåţîçåļļý ƀý ÅÎ (Ĵéṽ, Ĝéɱîñî). one two three four f]';
@override
- String get boardSortTop => '[Ţöþ on]';
+ String get boardReportPublicNotice => '[Κšûéš åŕé þûƀļîç. one tw]';
@override
- String get boardSortNew => '[Ñéŵ on]';
+ String boardReportSecurityNotice(String contact) {
+ return '[Šéçûŕîţý îššûé? Ðöñ’ţ þöšţ îţ ĥéŕé: éɱåîļ $contact one two three]';
+ }
@override
- String get boardSortDiscussed => '[Ṁöšţ ðîšçûššéð one t]';
+ String get boardReportPostBug => '[Þöšţ ƀûĝ one]';
@override
- String get boardSort => '[Šöŕţ on]';
+ String get boardReportPostIdea => '[Þöšţ îɱþŕöṽéɱéñţ one tw]';
@override
- String get boardFilterArea => '[Åŕéå on]';
+ String get boardReportPosted => '[Þöšţéð one]';
@override
- String get boardFilterSize => '[Šîžé on]';
+ String get boardReportPostedChecking => '[Þöšţéð. Çĥéçķîñĝ îţ ñöŵ… one two]';
@override
- String get boardFilterStatus => '[Šţåţûš one]';
+ String get boardReportPostedHeld =>
+ '[Þöšţéð. Ŵåîţîñĝ ƒöŕ ŕéṽîéŵ: å ɱöðéŕåţöŕ ŵîļļ ļööķ åţ îţ. one two three fou]';
@override
- String get boardFilterMine => '[Ṁîñé on]';
+ String get boardReportView => '[Ṽîéŵ on]';
@override
- String get boardFilterAny => '[Åñý on]';
+ String get boardReportRateLimited =>
+ '[Ýöû’ṽé þöšţéð å ļöţ ŕéçéñţļý. Þļéåšé ţŕý åĝåîñ ļåţéŕ. one two three fo]';
@override
- String get boardSelect => '[Šéļéçţ one]';
+ String get boardReportBlocked =>
+ '[Ýöû çåñ’ţ þöšţ öñ Ɓûĝš & îɱþŕöṽéɱéñţš. one two thre]';
@override
- String get boardSelectDone => '[Ðöñé šéļéçţîñĝ one t]';
+ String boardReportBlockedReason(String reason) {
+ return '[Ýöû çåñ’ţ þöšţ öñ Ɓûĝš & îɱþŕöṽéɱéñţš: $reason one two thre]';
+ }
@override
- String boardSelected(int count) {
- return '[$count šéļéçţéð one]';
+ String boardReportPostFailed(String message) {
+ return '[Çöûļðñ’ţ þöšţ: $message one t]';
}
@override
- String boardCreateTaskFrom(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: 'Çŕéåţé ţåšķ ƒŕöɱ $count îššûéš…',
- one: 'Çŕéåţé ţåšķ ƒŕöɱ 1 îššûé…',
- );
- return '[$_temp0 one two three f]';
+ String boardReportUploadFailed(String message) {
+ return '[Çöûļðñ’ţ ûþļöåð ţĥé šçŕééñšĥöţ: $message one two th]';
}
@override
- String get boardCreateTaskHint =>
- '[Ţĥé ðŕåƒţ ţåšķ ĝéţš åñ ÅÎ-ŵŕîţţéñ þŕöɱþţ. Îţ îš þûƀļîšĥéð öñçé ţĥé þŕöɱþţ îš ŵŕîţţéñ. one two three four five s]';
+ String get boardReportDiscardTitle => '[Ðîšçåŕð ðŕåƒţ? one t]';
@override
- String get boardAddToTask => '[Åðð ţö ţåšķ one]';
+ String get boardReportDiscardBody =>
+ '[Ýöûŕ îššûé ĥåšñ’ţ ƀééñ þöšţéð ýéţ. one two thr]';
@override
- String get boardAddToTaskEllipsis => '[Åðð ţö ţåšķ… one]';
+ String get boardReportDiscard => '[Ðîšçåŕð one]';
@override
- String get boardNoOpenTasks =>
- '[Ţĥéŕé åŕé ñö ðŕåƒţ öŕ þûƀļîšĥéð ţåšķš. one two thre]';
+ String get boardReportKeepDraft => '[Ķééþ ðŕåƒţ one]';
@override
- String get boardLoadMore => '[Ļöåð ɱöŕé one]';
+ String get errorReportBug => '[Ŕéþöŕţ ţĥîš ƀûĝ one t]';
@override
- String boardShowing(int shown, int total) {
- return '[$shown öƒ $total o]';
- }
+ String get screenshotEditorTitle => '[Éðîţ šçŕééñšĥöţ one t]';
@override
- String get boardLoadFailed => '[Çöûļðñ’ţ ļöåð Çöñţŕîƀûţé & ƀûîļð one two th]';
+ String get screenshotEditorWarning =>
+ '[Ţĥîš šçŕééñšĥöţ ŵîļļ ƀé þûƀļîç. Çöṽéŕ åñýţĥîñĝ þŕîṽåţé: ñåɱéš, éɱåîļš, ðöçûɱéñţ åñð šþŕéåðšĥééţ çöñţéñţ ýöû ðöñ’ţ ŵåñţ ţö šĥåŕé. one two three four five six seven eight]';
@override
- String get boardEmptyIdeas => '[Ñö îɱþŕöṽéɱéñţš ĥéŕé ýéţ one two]';
+ String get screenshotToolsLabel => '[Ţööļ on]';
@override
- String get boardEmptyBugs => '[Ñö ƀûĝš ĥéŕé one]';
+ String get screenshotColorsLabel => '[Çöļöŕ on]';
@override
- String get boardEmptyDone => '[Ñöţĥîñĝ ƒîñîšĥéð ýéţ one two]';
+ String get screenshotToolCover => '[Çöṽéŕ on]';
@override
- String get boardEmptySearch => '[Ñö îššûéš ɱåţçĥ ýöûŕ šéåŕçĥ one two t]';
+ String get screenshotToolHighlight => '[Ĥîĝĥļîĝĥţ one]';
@override
- String get boardEmptyHint =>
- '[Çĥåñĝé ţĥé ƒîļţéŕš, öŕ ƀé ţĥé ƒîŕšţ ţö ŕéþöŕţ öñé. one two three f]';
+ String get screenshotToolArrow => '[Åŕŕöŵ on]';
@override
- String get boardEmptyTasks => '[Ñö ţåšķš ûþ ƒöŕ ĝŕåƀš one tw]';
+ String get screenshotToolPen => '[Þéñ on]';
@override
- String get boardEmptyTasksHint =>
- '[Åðɱîñš þûƀļîšĥ ţåšķš ƒŕöɱ ţĥé ɱöšţ ŵåñţéð îššûéš. Çĥéçķ ƀåçķ šööñ. one two three four f]';
+ String get screenshotColorRed => '[Ŕéð on]';
@override
- String boardUpvoteLabel(int score) {
- return '[Ûþṽöţé, $score ṽöţéš one t]';
- }
+ String get screenshotColorYellow => '[Ýéļļöŵ one]';
@override
- String boardDownvoteLabel(int score) {
- return '[Ðöŵñṽöţé, $score ṽöţéš one t]';
- }
+ String get screenshotColorBlue => '[Ɓļûé on]';
@override
- String get boardRemoveUpvote => '[Ŕéɱöṽé ûþṽöţé one t]';
+ String get screenshotUndo => '[Ûñðö on]';
@override
- String get boardRemoveDownvote => '[Ŕéɱöṽé ðöŵñṽöţé one t]';
+ String get screenshotRedo => '[Ŕéðö on]';
@override
- String get boardBugsUpvoteOnly => '[Ɓûĝš çåñ öñļý ƀé ûþṽöţéð one two]';
+ String get screenshotDelete => '[Ðéļéţé šéļéçţéð one t]';
@override
- String get boardDownvoteUnavailable =>
- '[Ðöŵñṽöţé, ûñåṽåîļåƀļé: ƀûĝš çåñ öñļý ƀé ûþṽöţéð one two three f]';
+ String get screenshotClear => '[Çļéåŕ on]';
@override
- String get boardAiMarker => '[ÅÎ o]';
+ String get screenshotDone => '[Ðöñé on]';
@override
- String boardAiLabel(String label) {
- return '[$label, šéţ ƀý ÅÎ one]';
+ String screenshotShortcut(String label, String shortcut) {
+ return '[$label ($shortcut) o]';
}
@override
- String boardPlannedIn(int number) {
- return '[Þļåññéð · Ţ-$number one]';
- }
+ String get screenshotCanvasLabel =>
+ '[Šçŕééñšĥöţ. Ðŕåĝ ţö ðŕåŵ ŵîţĥ ţĥé šéļéçţéð ţööļ; çļîçķ åñ åññöţåţîöñ ţö šéļéçţ îţ. one two three four five s]';
@override
- String boardCommentCount(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '$count çöɱɱéñţš',
- one: '1 çöɱɱéñţ',
- );
- return '[$_temp0 one tw]';
+ String screenshotEncodeFailed(String message) {
+ return '[Çöûļðñ’ţ þŕéþåŕé ţĥé šçŕééñšĥöţ: $message one two thr]';
}
@override
- String get boardHasScreenshot => '[Ĥåš å šçŕééñšĥöţ one t]';
+ String get boardTitle => '[Çöñţŕîƀûţé & ƀûîļð one tw]';
@override
- String get boardDeletedUser => '[Ðéļéţéð ûšéŕ one]';
+ String get boardBreadcrumb => '[Ɓŕéåðçŕûɱƀ one]';
@override
- String get boardAdmin => '[Åðɱîñ on]';
+ String get boardClose => '[Çļöšé Çöñţŕîƀûţé & ƀûîļð one two]';
@override
- String boardEdited(String time) {
- return '[éðîţéð $time one]';
+ String get boardShowPath => '[Šĥöŵ ţĥé ƒûļļ þåţĥ one tw]';
+
+ @override
+ String boardTabWithCount(String name, int count) {
+ return '[$name, $count o]';
}
@override
- String get boardCopy => '[Çöþý on]';
+ String get boardReportBug => '[Ŕéþöŕţ å ƀûĝ one]';
@override
- String get boardCopied => '[Çöþîéð one]';
+ String get boardSuggestImprovement => '[Šûĝĝéšţ åñ îɱþŕöṽéɱéñţ one two]';
@override
- String get boardPromptCopied => '[Þŕöɱþţ çöþîéð one t]';
+ String get boardSearch => '[Šéåŕçĥ îššûéš one t]';
@override
- String get boardFlagged =>
- '[Ţĥåñķš. Å ɱöðéŕåţöŕ ŵîļļ ţåķé å ļööķ. one two thr]';
+ String get boardSignIn => '[Šîĝñ îñ one]';
@override
- String boardPostNotFound(int number) {
- return '[Κšûé #$number ðöéšñ\'ţ éẋîšţ öŕ îšñ\'ţ þûƀļîç one two thr]';
- }
+ String get boardSignInToVote =>
+ '[Šîĝñ îñ ţö ṽöţé åñð ŕéþöŕţ îššûéš. one two th]';
@override
- String boardTaskNotFound(int number) {
- return '[Ţåšķ Ţ-$number ðöéšñ\'ţ éẋîšţ one two]';
- }
+ String get boardSignInToComment => '[Šîĝñ îñ ţö çöɱɱéñţ one tw]';
@override
- String get boardBackToBoard => '[Ɓåçķ ţö Çöñţŕîƀûţé & ƀûîļð one two]';
+ String get boardSignInToClaim => '[Šîĝñ îñ ţö çļåîɱ ţĥîš ţåšķ one two]';
@override
- String get boardAdminActions => '[Åðɱîñ on]';
+ String get boardBlocked =>
+ '[Ýöû çåñ\'ţ þöšţ öñ Ɓûĝš & îɱþŕöṽéɱéñţš. one two thre]';
@override
- String get boardEditAndApprove => '[Éðîţ åñð åþþŕöṽé… one tw]';
+ String get boardContributeLink => '[Ĥöŵ ţö çöñţŕîƀûţé one tw]';
@override
- String get boardActionApprove => '[Åþþŕöṽé one]';
+ String get boardTabImprovements => '[Îɱþŕöṽéɱéñţš one t]';
@override
- String get boardActionHold => '[Ĥöļð on]';
+ String get boardTabBugs => '[Ɓûĝš on]';
@override
- String get boardActionLooksFine => '[Ļööķš ƒîñé one]';
+ String get boardTabTasks => '[Ûþ ƒöŕ ĝŕåƀš one]';
@override
- String get boardActionRestore => '[Ŕéšţöŕé one]';
+ String get boardTabClaimed => '[Çļåîɱéð one]';
@override
- String get boardActionRemove => '[Ŕéɱöṽé one]';
+ String get boardSearchButton => '[Šéåŕçĥ one]';
@override
- String get boardActionRemoveEllipsis => '[Ŕéɱöṽé… one]';
+ String boardSearchActive(String query) {
+ return '[Šéåŕçĥ: “$query” one]';
+ }
@override
- String get boardActionDecline => '[Ðéçļîñé one]';
+ String get boardSearchRemove => '[Çļéåŕ ţĥé šéåŕçĥ one t]';
@override
- String get boardActionMerge => '[Ṁéŕĝé on]';
+ String get boardSearchIncludeClosed => '[Îñçļûðé çļöšéð one t]';
@override
- String get boardActionBlock => '[Ɓļöçķ on]';
+ String get boardSearchIncludeClosedHint =>
+ '[Ðöñé, šĥîþþéð, ðéçļîñéð åñð ðûþļîçåţéš one two thre]';
@override
- String get boardActionCreateTask => '[Çŕéåţé ţåšķ one]';
+ String get boardSearchKind => '[Ķîñð on]';
@override
- String get boardActionAdd => '[Åðð on]';
+ String get boardSearchKindAll => '[Åļļ on]';
@override
- String get boardActionLink => '[Ļîñķ on]';
+ String get boardSearchResults => '[Šéåŕçĥ one]';
@override
- String get boardSelectForBuilding => '[Šéļéçţ ƒöŕ ƀûîļðîñĝ one tw]';
+ String boardSearchResultsFor(String query) {
+ return '[Ŕéšûļţš ƒöŕ “$query” one t]';
+ }
@override
- String get boardDeclineEllipsis => '[Ðéçļîñé… one]';
+ String get boardSearchStartTitle => '[Šéåŕçĥ ţĥé ƀöåŕð one t]';
@override
- String get boardMergeEllipsis => '[Ṁéŕĝé îñţö… one]';
+ String get boardSearchStartHint =>
+ '[Ƒîñð îššûéš ƀý ţĥéîŕ ŵöŕðš, þåŕţ öƒ å ţîţļé öŕ ţĥéîŕ ñûɱƀéŕ. one two three four]';
@override
- String get boardLabelsEllipsis => '[Ļåƀéļš… one]';
+ String get boardEmptySearchHint =>
+ '[Ţŕý ƒéŵéŕ ŵöŕðš öŕ þåŕţ öƒ å ŵöŕð, öŕ îñçļûðé çļöšéð îššûéš. one two three four]';
@override
- String get boardBlockEllipsis => '[Ɓļöçķ ûšéŕ ƒŕöɱ ţĥé ƀöåŕð… one two]';
+ String get boardEmptyClaimed => '[Ñö çļåîɱéð ţåšķš one t]';
@override
- String get boardEditEllipsis => '[Éðîţ… on]';
+ String get boardEmptyClaimedHint =>
+ '[Ţåšķš šĥöŵ ĥéŕé ŵĥîļé šöɱéöñé ŵöŕķš öñ ţĥéɱ, åñð ŵĥîļé ţĥéîŕ þûļļ ŕéǫûéšţ îš îñ ŕéṽîéŵ. one two three four five si]';
@override
- String get boardDismiss => '[Ðîšɱîšš one]';
+ String get boardTabDone => '[Ðöñé on]';
@override
- String get boardApply => '[Åþþļý on]';
+ String get boardTabReview => '[Ŕéṽîéŵ one]';
@override
- String boardMergedFromNote(int number) {
- return '[#$number ŵåš ɱéŕĝéð îñţö ţĥîš îššûé. one two t]';
- }
+ String get boardKindBug => '[Ɓûĝ on]';
@override
- String get boardPostDeleted => '[Ţĥîš îššûé ŵåš ðéļéţéð. one two]';
+ String get boardKindIdea => '[Îɱþŕöṽéɱéñţ one]';
@override
- String get boardChecking => '[Çĥéçķîñĝ ýöûŕ îššûé… one two]';
+ String get boardAreaDrive => '[Ðŕîṽé on]';
@override
- String get boardWaitingForReview =>
- '[Ŵåîţîñĝ ƒöŕ ŕéṽîéŵ: å ɱöðéŕåţöŕ ŵîļļ ļööķ åţ îţ. one two three]';
+ String get boardAreaWrite => '[Ŵŕîţé on]';
@override
- String boardHeldAdmin(String reason) {
- return '[Ĥéļð ƒöŕ ŕéṽîéŵ. $reason one t]';
- }
+ String get boardAreaSheets => '[Šĥééţš one]';
@override
- String boardRemovedByModerator(String reason) {
- return '[Ŕéɱöṽéð ƀý å ɱöðéŕåţöŕ. $reason one two]';
- }
+ String get boardAreaSlides => '[Šļîðéš one]';
@override
- String boardDeclinedNote(String reason) {
- return '[Ðéçļîñéð: $reason one]';
- }
+ String get boardAreaSuite => '[Šûîţé on]';
@override
- String boardMergedInto(int number) {
- return '[Ṁéŕĝéð îñţö #$number. one t]';
- }
+ String get boardAreaServer => '[Šéŕṽéŕ one]';
@override
- String boardOpenPost(int number) {
- return '[Öþéñ #$number on]';
- }
+ String get boardSizeSmall => '[Šɱåļļ on]';
@override
- String get boardMovedToIdeas => '[Ṁöṽéð ţö Îɱþŕöṽéɱéñţš one two]';
+ String get boardSizeMedium => '[Ṁéðîûɱ one]';
@override
- String get boardMovedToBugs => '[Ṁöṽéð ţö Ɓûĝš one]';
+ String get boardSizeLarge => '[Ļåŕĝé on]';
@override
- String get boardEditedByModerator => '[Éðîţéð ƀý å ɱöðéŕåţöŕ one two]';
+ String get boardStatusOpen => '[Öþéñ on]';
@override
- String get boardDetails => '[Ðéţåîļš one]';
+ String get boardStatusPlanned => '[Þļåññéð one]';
@override
- String get boardAdminPanel => '[Ṁöðéŕåţîöñ one]';
+ String get boardStatusDone => '[Ðöñé on]';
@override
- String boardTriage(String state) {
- return '[Ţŕîåĝé: $state one]';
- }
+ String get boardStatusShipped => '[Šĥîþþéð one]';
@override
- String get boardNeedsCheck => '[Çĥéçķ on]';
+ String get boardStatusDeclined => '[Ðéçļîñéð one]';
@override
- String get boardNeedsLabels => '[Ñééðš ļåƀéļš one]';
+ String get boardStatusDuplicate => '[Ðûþļîçåţé one]';
@override
- String boardSuggestedTitle(String title) {
- return '[Šûĝĝéšţéð ţîţļé: $title one tw]';
- }
+ String get boardTaskDraft => '[Ðŕåƒţ on]';
@override
- String boardPossibleDuplicate(int number) {
- return '[Þöššîƀļé ðûþļîçåţé öƒ #$number one two]';
- }
+ String get boardTaskPublished => '[Þûƀļîšĥéð one]';
@override
- String get boardAiScores => '[ÅÎ šçöŕéš one]';
+ String get boardTaskClaimed => '[Çļåîɱéð one]';
@override
- String boardFlags(int count, String reasons) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '$count ƒļåĝš',
- one: '1 ƒļåĝ',
- );
- return '[$_temp0: $reasons one]';
- }
+ String get boardTaskInReview => '[Îñ ŕéṽîéŵ one]';
@override
- String get boardOpenScreenshot => '[Öþéñ šçŕééñšĥöţ one t]';
+ String get boardTaskCancelled => '[Çåñçéļļéð one]';
@override
- String boardScreenshotAlt(int number) {
- return '[Šçŕééñšĥöţ öƒ îššûé #$number one two]';
- }
+ String get boardModerationChecking => '[Çĥéçķîñĝ one]';
@override
- String get boardRelations => '[Ŕéļåţéð one]';
+ String get boardModerationVisible => '[Ṽîšîƀļé one]';
@override
- String boardPartOfTask(int number, String title) {
- return '[Þåŕţ öƒ ţåšķ Ţ-$number: $title one t]';
- }
+ String get boardModerationHeld => '[Ĥéļð on]';
@override
- String get boardMergedFrom =>
- '[Ðûþļîçåţéš ɱéŕĝéð îñţö ţĥîš îššûé: one two thr]';
+ String get boardModerationRemoved => '[Ŕéɱöṽéð one]';
@override
- String get boardSimilarPosts => '[Šîɱîļåŕ îššûéš one t]';
+ String get boardReasonOffensive => '[Öƒƒéñšîṽé one]';
@override
- String boardScore(int score) {
- String _temp0 = intl.Intl.pluralLogic(
- score,
- locale: localeName,
- other: '$score ṽöţéš',
- one: '1 ṽöţé',
- );
- return '[$_temp0 one]';
- }
+ String get boardReasonSpam => '[Šþåɱ on]';
@override
- String boardCommentsTitle(int count) {
- return '[Çöɱɱéñţš ($count) one]';
- }
+ String get boardReasonPersonalData => '[Þéŕšöñåļ ðåţå one t]';
@override
- String get boardCommentHint => '[Åðð å çöɱɱéñţ one]';
+ String get boardReasonSecurity => '[Šéçûŕîţý îššûé one t]';
@override
- String get boardCommentHeld => '[Ĥîððéñ þéñðîñĝ ŕéṽîéŵ one two]';
+ String get boardReasonOther => '[Öţĥéŕ on]';
@override
- String get boardCommentRemoved => '[Ŕéɱöṽéð ƀý å ɱöðéŕåţöŕ one two]';
+ String get boardSortTrending => '[Ţŕéñðîñĝ one]';
@override
- String boardCommentOnPost(int number) {
- return '[Çöɱɱéñţ öñ #$number one]';
- }
+ String get boardSortTop => '[Ţöþ on]';
@override
- String boardCommentOnTask(int number) {
- return '[Çöɱɱéñţ öñ Ţ-$number one]';
- }
+ String get boardSortNew => '[Ñéŵ on]';
@override
- String get boardEventClaimed => '[çļåîɱéð ţĥé ţåšķ one t]';
+ String get boardSortDiscussed => '[Ṁöšţ ðîšçûššéð one t]';
@override
- String get boardEventReleased => '[ŕéļéåšéð ţĥé çļåîɱ one tw]';
+ String get boardSort => '[Šöŕţ on]';
@override
- String get boardEventExpired => '[Ţĥé çļåîɱ éẋþîŕéð one tw]';
+ String get boardFilterArea => '[Åŕéå on]';
@override
- String boardEventPrLinked(String url) {
- return '[ļîñķéð å þûļļ ŕéǫûéšţ: $url one two]';
- }
+ String get boardFilterSize => '[Šîžé on]';
@override
- String get boardEventPrUnlinked => '[ûñļîñķéð ţĥé þûļļ ŕéǫûéšţ one two]';
+ String get boardFilterStatus => '[Šţåţûš one]';
@override
- String get boardEventPublished => '[þûƀļîšĥéð ţĥé ţåšķ one tw]';
+ String get boardFilterMine => '[Ṁîñé on]';
@override
- String get boardEventDone => '[ɱåŕķéð ţĥé ţåšķ ðöñé one tw]';
+ String get boardFilterAny => '[Åñý on]';
@override
- String get boardEventShipped => '[ɱåŕķéð ţĥé ţåšķ šĥîþþéð one two]';
+ String get boardSelect => '[Šéļéçţ one]';
@override
- String get boardEventCancelled => '[çåñçéļļéð ţĥé ţåšķ one tw]';
+ String get boardSelectDone => '[Ðöñé šéļéçţîñĝ one t]';
@override
- String get boardEventMerged => '[ɱéŕĝéð å ðûþļîçåţé one tw]';
+ String boardSelected(int count) {
+ return '[$count šéļéçţéð one]';
+ }
@override
- String boardEventMergedInto(String task) {
- return '[ɱéŕĝéð ţĥîš ţåšķ îñţö $task one two]';
+ String boardCreateTaskFrom(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'Çŕéåţé ţåšķ ƒŕöɱ $count îššûéš…',
+ one: 'Çŕéåţé ţåšķ ƒŕöɱ 1 îššûé…',
+ );
+ return '[$_temp0 one two three f]';
}
@override
- String boardEventMergedTasks(String tasks) {
- return '[ɱéŕĝéð $tasks îñţö ţĥîš ţåšķ one two]';
- }
+ String get boardCreateTaskHint =>
+ '[Ţĥé ðŕåƒţ ţåšķ ĝéţš åñ ÅÎ-ŵŕîţţéñ þŕöɱþţ. Îţ îš þûƀļîšĥéð öñçé ţĥé þŕöɱþţ îš ŵŕîţţéñ. one two three four five s]';
@override
- String get boardEventUnmerged => '[ûñðîð å ɱéŕĝé öƒ ţåšķš one two]';
+ String get boardAddToTask => '[Åðð ţö ţåšķ one]';
@override
- String get boardEventRewriting =>
- '[çĥåñĝéð ţĥé îššûéš; ţĥé ţåšķ îš å ðŕåƒţ ûñţîļ ţĥé ÅÎ ĥåš ŕéŵŕîţţéñ ţĥé þŕöɱþţ one two three four five]';
+ String get boardAddToTaskEllipsis => '[Åðð ţö ţåšķ… one]';
@override
- String boardMergeTasksTitle(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: 'Ṁéŕĝé $count ţåšķš…',
- one: 'Ṁéŕĝé 1 ţåšķ…',
- );
- return '[$_temp0 one two]';
+ String get boardNoOpenTasks =>
+ '[Ţĥéŕé åŕé ñö ðŕåƒţ öŕ þûƀļîšĥéð ţåšķš. one two thre]';
+
+ @override
+ String get boardLoadMore => '[Ļöåð ɱöŕé one]';
+
+ @override
+ String boardShowing(int shown, int total) {
+ return '[$shown öƒ $total o]';
}
@override
- String get boardMergeTasksHint =>
- '[Ţĥé öļðéšţ ţåšķ ķééþš åļļ ţĥé îššûéš åñð ĝéţš å ñéŵ ÅÎ-ŵŕîţţéñ þŕöɱþţ. Ţĥé öţĥéŕ ţåšķš åŕé çåñçéļļéð. Ûñţîļ ţĥé þŕöɱþţ îš ŵŕîţţéñ, ţĥé ţåšķ îš å ðŕåƒţ. one two three four five six seven eight nine]';
+ String get boardLoadFailed => '[Çöûļðñ’ţ ļöåð Çöñţŕîƀûţé & ƀûîļð one two th]';
@override
- String get boardMergeTasksNeedsTwo =>
- '[Šéļéçţ ţŵö ţåšķš öŕ ɱöŕé ţö ɱéŕĝé ţĥéɱ one two thr]';
+ String get boardEmptyIdeas => '[Ñö îɱþŕöṽéɱéñţš ĥéŕé ýéţ one two]';
@override
- String boardTaskMergedNotice(int number) {
- return '[Ţĥîš ţåšķ ŵåš ɱéŕĝéð îñţö Ţ-$number. one two t]';
+ String get boardEmptyBugs => '[Ñö ƀûĝš ĥéŕé one]';
+
+ @override
+ String get boardEmptyDone => '[Ñöţĥîñĝ ƒîñîšĥéð ýéţ one two]';
+
+ @override
+ String get boardEmptySearch => '[Ñö îššûéš ɱåţçĥ ýöûŕ šéåŕçĥ one two t]';
+
+ @override
+ String get boardEmptyHint =>
+ '[Çĥåñĝé ţĥé ƒîļţéŕš, öŕ ƀé ţĥé ƒîŕšţ ţö ŕéþöŕţ öñé. one two three f]';
+
+ @override
+ String get boardEmptyTasks => '[Ñö ţåšķš ûþ ƒöŕ ĝŕåƀš one tw]';
+
+ @override
+ String get boardEmptyTasksHint =>
+ '[Åðɱîñš þûƀļîšĥ ţåšķš ƒŕöɱ ţĥé ɱöšţ ŵåñţéð îššûéš. Çĥéçķ ƀåçķ šööñ. one two three four f]';
+
+ @override
+ String boardUpvoteLabel(int score) {
+ return '[Ûþṽöţé, $score ṽöţéš one t]';
}
@override
- String boardOpenTask(int number) {
- return '[Öþéñ Ţ-$number one]';
+ String boardDownvoteLabel(int score) {
+ return '[Ðöŵñṽöţé, $score ṽöţéš one t]';
}
@override
- String get cmdBoardMergeTasks => '[Ṁéŕĝé ţåšķš one]';
+ String get boardRemoveUpvote => '[Ŕéɱöṽé ûþṽöţé one t]';
@override
- String get boardUpForGrabs => '[Ûþ ƒöŕ ĝŕåƀš one]';
+ String get boardRemoveDownvote => '[Ŕéɱöṽé ðöŵñṽöţé one t]';
@override
- String boardClaimedBy(String name, int hours) {
- return '[Çļåîɱéð ƀý $name · $hours ĥ ļéƒţ one tw]';
+ String get boardBugsUpvoteOnly => '[Ɓûĝš çåñ öñļý ƀé ûþṽöţéð one two]';
+
+ @override
+ String get boardDownvoteUnavailable =>
+ '[Ðöŵñṽöţé, ûñåṽåîļåƀļé: ƀûĝš çåñ öñļý ƀé ûþṽöţéð one two three f]';
+
+ @override
+ String get boardAiMarker => '[ÅÎ o]';
+
+ @override
+ String boardAiLabel(String label) {
+ return '[$label, šéţ ƀý ÅÎ one]';
}
@override
- String boardInReviewBy(String name) {
- return '[Îñ ŕéṽîéŵ · $name one]';
+ String boardPlannedIn(int number) {
+ return '[Þļåññéð · Ţ-$number one]';
}
@override
- String boardPostCount(int count) {
+ String boardCommentCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
- other: '$count îššûéš',
- one: '1 îššûé',
+ other: '$count çöɱɱéñţš',
+ one: '1 çöɱɱéñţ',
);
- return '[$_temp0 one t]';
+ return '[$_temp0 one tw]';
}
@override
- String get boardClaimTask => '[Çļåîɱ ţĥîš ţåšķ one t]';
+ String get boardHasScreenshot => '[Ĥåš å šçŕééñšĥöţ one t]';
@override
- String get boardReleaseClaim => '[Ŕéļéåšé çļåîɱ one t]';
+ String get boardDeletedUser => '[Ðéļéţéð ûšéŕ one]';
@override
- String get boardYourClaimCode => '[Ýöûŕ çļåîɱ çöðé one t]';
+ String get boardAdmin => '[Åðɱîñ on]';
@override
- String boardTimeLeft(int hours) {
- return '[$hours ĥ ļéƒţ öñ ýöûŕ çļåîɱ one tw]';
+ String boardEdited(String time) {
+ return '[éðîţéð $time one]';
}
@override
- String get boardClaimCodeHint =>
- '[Þûţ îţ îñ ţĥé þûļļ ŕéǫûéšţ ðéšçŕîþţîöñ. Öñļý ýöû çåñ šéé îţ. one two three four]';
+ String get boardCopy => '[Çöþý on]';
@override
- String get boardClaimCodePlaceholder => '[<ýöûŕ çļåîɱ çöðé> one tw]';
+ String get boardCopied => '[Çöþîéð one]';
@override
- String get boardDraftNotice =>
- '[Ðŕåƒţ: öñļý åðɱîñš šéé ţĥîš ţåšķ. Îţ îš þûƀļîšĥéð öñçé ţĥé ÅÎ ĥåš ŵŕîţţéñ îţš þŕöɱþţ, öŕ ŵĥéñ ýöû šåṽé ţĥé þŕöɱþţ. one two three four five six seven]';
+ String get boardPromptCopied => '[Þŕöɱþţ çöþîéð one t]';
@override
- String get boardPromptWriting =>
- '[Ŵŕîţîñĝ ţĥé þŕöɱþţ ŵîţĥ ÅÎ… ţĥîš ţåķéš åƀöûţ å ɱîñûţé one two three fo]';
+ String get boardFlagged =>
+ '[Ţĥåñķš. Å ɱöðéŕåţöŕ ŵîļļ ţåķé å ļööķ. one two thr]';
@override
- String boardPromptNote(String note) {
- return '[Ţĥé ÅÎ ðîð ñöţ ŵŕîţé ţĥé þŕöɱþţ: $note one two th]';
+ String boardPostNotFound(int number) {
+ return '[Κšûé #$number ðöéšñ\'ţ éẋîšţ öŕ îšñ\'ţ þûƀļîç one two thr]';
}
@override
- String get boardTaskCancelledNotice =>
- '[Ţĥîš ţåšķ ŵåš çåñçéļļéð. Îţš îššûéš åŕé öþéñ åĝåîñ. one two three fo]';
+ String boardTaskNotFound(int number) {
+ return '[Ţåšķ Ţ-$number ðöéšñ\'ţ éẋîšţ one two]';
+ }
@override
- String get boardLinkedPosts => '[Ļîñķéð îššûéš one t]';
+ String get boardBackToBoard => '[Ɓåçķ ţö Çöñţŕîƀûţé & ƀûîļð one two]';
@override
- String get boardNoLinkedPosts =>
- '[Ñö îššûéš åŕé ļîñķéð ţö ţĥîš ţåšķ. one two th]';
+ String get boardAdminActions => '[Åðɱîñ on]';
@override
- String get boardRemoveFromTask => '[Ŕéɱöṽé ƒŕöɱ ţåšķ one t]';
+ String get boardEditAndApprove => '[Éðîţ åñð åþþŕöṽé… one tw]';
@override
- String get boardAddPosts => '[Åðð îššûéš… one]';
+ String get boardActionApprove => '[Åþþŕöṽé one]';
@override
- String get boardFieldPostNumbers => '[Κšûé ñûɱƀéŕš one t]';
+ String get boardActionHold => '[Ĥöļð on]';
@override
- String get boardHowToBuild => '[Ĥöŵ ţö ƀûîļð îţ one t]';
+ String get boardActionLooksFine => '[Ļööķš ƒîñé one]';
@override
- String get boardStepSetupNumber => '[1. o]';
+ String get boardActionRestore => '[Ŕéšţöŕé one]';
@override
- String get boardStepSetupLink =>
- '[Šéţ ûþ ýöûŕ ðéṽéļöþɱéñţ éñṽîŕöñɱéñţ one two thr]';
+ String get boardActionRemove => '[Ŕéɱöṽé one]';
@override
- String get boardInstallTools =>
- '[Îñšţåļļ Ƒļûţţéŕ, Ðåŕţ åñð ţĥé Šéŕṽéŕþöð ÇĻÎ: one two three]';
+ String get boardActionRemoveEllipsis => '[Ŕéɱöṽé… one]';
+
+ @override
+ String get boardActionDecline => '[Ðéçļîñé one]';
+
+ @override
+ String get boardActionMerge => '[Ṁéŕĝé on]';
+
+ @override
+ String get boardActionBlock => '[Ɓļöçķ on]';
+
+ @override
+ String get boardActionCreateTask => '[Çŕéåţé ţåšķ one]';
+
+ @override
+ String get boardActionAdd => '[Åðð on]';
+
+ @override
+ String get boardActionLink => '[Ļîñķ on]';
+
+ @override
+ String get boardSelectForBuilding => '[Šéļéçţ ƒöŕ ƀûîļðîñĝ one tw]';
+
+ @override
+ String get boardDeclineEllipsis => '[Ðéçļîñé… one]';
+
+ @override
+ String get boardMergeEllipsis => '[Ṁéŕĝé îñţö… one]';
+
+ @override
+ String get boardLabelsEllipsis => '[Ļåƀéļš… one]';
+
+ @override
+ String get boardBlockEllipsis => '[Ɓļöçķ ûšéŕ ƒŕöɱ ţĥé ƀöåŕð… one two]';
+
+ @override
+ String get boardEditEllipsis => '[Éðîţ… on]';
+
+ @override
+ String get boardDismiss => '[Ðîšɱîšš one]';
+
+ @override
+ String get boardApply => '[Åþþļý on]';
+
+ @override
+ String boardMergedFromNote(int number) {
+ return '[#$number ŵåš ɱéŕĝéð îñţö ţĥîš îššûé. one two t]';
+ }
+
+ @override
+ String get boardPostDeleted => '[Ţĥîš îššûé ŵåš ðéļéţéð. one two]';
+
+ @override
+ String get boardChecking => '[Çĥéçķîñĝ ýöûŕ îššûé… one two]';
+
+ @override
+ String get boardWaitingForReview =>
+ '[Ŵåîţîñĝ ƒöŕ ŕéṽîéŵ: å ɱöðéŕåţöŕ ŵîļļ ļööķ åţ îţ. one two three]';
+
+ @override
+ String boardHeldAdmin(String reason) {
+ return '[Ĥéļð ƒöŕ ŕéṽîéŵ. $reason one t]';
+ }
+
+ @override
+ String boardRemovedByModerator(String reason) {
+ return '[Ŕéɱöṽéð ƀý å ɱöðéŕåţöŕ. $reason one two]';
+ }
+
+ @override
+ String boardDeclinedNote(String reason) {
+ return '[Ðéçļîñéð: $reason one]';
+ }
+
+ @override
+ String boardMergedInto(int number) {
+ return '[Ṁéŕĝéð îñţö #$number. one t]';
+ }
+
+ @override
+ String boardOpenPost(int number) {
+ return '[Öþéñ #$number on]';
+ }
+
+ @override
+ String get boardMovedToIdeas => '[Ṁöṽéð ţö Îɱþŕöṽéɱéñţš one two]';
+
+ @override
+ String get boardMovedToBugs => '[Ṁöṽéð ţö Ɓûĝš one]';
+
+ @override
+ String get boardEditedByModerator => '[Éðîţéð ƀý å ɱöðéŕåţöŕ one two]';
+
+ @override
+ String get boardDetails => '[Ðéţåîļš one]';
+
+ @override
+ String get boardAdminPanel => '[Ṁöðéŕåţîöñ one]';
+
+ @override
+ String boardTriage(String state) {
+ return '[Ţŕîåĝé: $state one]';
+ }
+
+ @override
+ String get boardNeedsCheck => '[Çĥéçķ on]';
+
+ @override
+ String get boardNeedsLabels => '[Ñééðš ļåƀéļš one]';
+
+ @override
+ String boardSuggestedTitle(String title) {
+ return '[Šûĝĝéšţéð ţîţļé: $title one tw]';
+ }
+
+ @override
+ String boardPossibleDuplicate(int number) {
+ return '[Þöššîƀļé ðûþļîçåţé öƒ #$number one two]';
+ }
+
+ @override
+ String get boardAiScores => '[ÅÎ šçöŕéš one]';
+
+ @override
+ String boardFlags(int count, String reasons) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count ƒļåĝš',
+ one: '1 ƒļåĝ',
+ );
+ return '[$_temp0: $reasons one]';
+ }
+
+ @override
+ String get boardOpenScreenshot => '[Öþéñ šçŕééñšĥöţ one t]';
+
+ @override
+ String boardScreenshotAlt(int number) {
+ return '[Šçŕééñšĥöţ öƒ îššûé #$number one two]';
+ }
+
+ @override
+ String get boardRelations => '[Ŕéļåţéð one]';
+
+ @override
+ String boardPartOfTask(int number, String title) {
+ return '[Þåŕţ öƒ ţåšķ Ţ-$number: $title one t]';
+ }
+
+ @override
+ String get boardMergedFrom =>
+ '[Ðûþļîçåţéš ɱéŕĝéð îñţö ţĥîš îššûé: one two thr]';
+
+ @override
+ String get boardSimilarPosts => '[Šîɱîļåŕ îššûéš one t]';
+
+ @override
+ String boardScore(int score) {
+ String _temp0 = intl.Intl.pluralLogic(
+ score,
+ locale: localeName,
+ other: '$score ṽöţéš',
+ one: '1 ṽöţé',
+ );
+ return '[$_temp0 one]';
+ }
+
+ @override
+ String boardCommentsTitle(int count) {
+ return '[Çöɱɱéñţš ($count) one]';
+ }
+
+ @override
+ String get boardCommentHint => '[Åðð å çöɱɱéñţ one]';
+
+ @override
+ String get boardCommentHeld => '[Ĥîððéñ þéñðîñĝ ŕéṽîéŵ one two]';
+
+ @override
+ String get boardCommentRemoved => '[Ŕéɱöṽéð ƀý å ɱöðéŕåţöŕ one two]';
+
+ @override
+ String boardCommentOnPost(int number) {
+ return '[Çöɱɱéñţ öñ #$number one]';
+ }
+
+ @override
+ String boardCommentOnTask(int number) {
+ return '[Çöɱɱéñţ öñ Ţ-$number one]';
+ }
+
+ @override
+ String get boardEventClaimed => '[çļåîɱéð ţĥé ţåšķ one t]';
+
+ @override
+ String get boardEventReleased => '[ŕéļéåšéð ţĥé çļåîɱ one tw]';
+
+ @override
+ String get boardEventExpired => '[Ţĥé çļåîɱ éẋþîŕéð one tw]';
+
+ @override
+ String boardEventPrLinked(String url) {
+ return '[ļîñķéð å þûļļ ŕéǫûéšţ: $url one two]';
+ }
+
+ @override
+ String get boardEventPrUnlinked => '[ûñļîñķéð ţĥé þûļļ ŕéǫûéšţ one two]';
+
+ @override
+ String get boardEventPublished => '[þûƀļîšĥéð ţĥé ţåšķ one tw]';
+
+ @override
+ String get boardEventDone => '[ɱåŕķéð ţĥé ţåšķ ðöñé one tw]';
+
+ @override
+ String get boardEventShipped => '[ɱåŕķéð ţĥé ţåšķ šĥîþþéð one two]';
+
+ @override
+ String get boardEventCancelled => '[çåñçéļļéð ţĥé ţåšķ one tw]';
+
+ @override
+ String get boardEventMerged => '[ɱéŕĝéð å ðûþļîçåţé one tw]';
+
+ @override
+ String boardEventMergedInto(String task) {
+ return '[ɱéŕĝéð ţĥîš ţåšķ îñţö $task one two]';
+ }
+
+ @override
+ String boardEventMergedTasks(String tasks) {
+ return '[ɱéŕĝéð $tasks îñţö ţĥîš ţåšķ one two]';
+ }
+
+ @override
+ String get boardEventUnmerged => '[ûñðîð å ɱéŕĝé öƒ ţåšķš one two]';
+
+ @override
+ String get boardEventRewriting =>
+ '[çĥåñĝéð ţĥé îššûéš; ţĥé ţåšķ îš å ðŕåƒţ ûñţîļ ţĥé ÅÎ ĥåš ŕéŵŕîţţéñ ţĥé þŕöɱþţ one two three four five]';
+
+ @override
+ String boardMergeTasksTitle(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'Ṁéŕĝé $count ţåšķš…',
+ one: 'Ṁéŕĝé 1 ţåšķ…',
+ );
+ return '[$_temp0 one two]';
+ }
+
+ @override
+ String get boardMergeTasksHint =>
+ '[Ţĥé öļðéšţ ţåšķ ķééþš åļļ ţĥé îššûéš åñð ĝéţš å ñéŵ ÅÎ-ŵŕîţţéñ þŕöɱþţ. Ţĥé öţĥéŕ ţåšķš åŕé çåñçéļļéð. Ûñţîļ ţĥé þŕöɱþţ îš ŵŕîţţéñ, ţĥé ţåšķ îš å ðŕåƒţ. one two three four five six seven eight nine]';
+
+ @override
+ String get boardMergeTasksNeedsTwo =>
+ '[Šéļéçţ ţŵö ţåšķš öŕ ɱöŕé ţö ɱéŕĝé ţĥéɱ one two thr]';
+
+ @override
+ String boardTaskMergedNotice(int number) {
+ return '[Ţĥîš ţåšķ ŵåš ɱéŕĝéð îñţö Ţ-$number. one two t]';
+ }
+
+ @override
+ String boardOpenTask(int number) {
+ return '[Öþéñ Ţ-$number one]';
+ }
+
+ @override
+ String get cmdBoardMergeTasks => '[Ṁéŕĝé ţåšķš one]';
+
+ @override
+ String get boardUpForGrabs => '[Ûþ ƒöŕ ĝŕåƀš one]';
+
+ @override
+ String boardClaimedBy(String name, int hours) {
+ return '[Çļåîɱéð ƀý $name · $hours ĥ ļéƒţ one tw]';
+ }
+
+ @override
+ String boardInReviewBy(String name) {
+ return '[Îñ ŕéṽîéŵ · $name one]';
+ }
+
+ @override
+ String boardPostCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count îššûéš',
+ one: '1 îššûé',
+ );
+ return '[$_temp0 one t]';
+ }
+
+ @override
+ String get boardClaimTask => '[Çļåîɱ ţĥîš ţåšķ one t]';
+
+ @override
+ String get boardReleaseClaim => '[Ŕéļéåšé çļåîɱ one t]';
+
+ @override
+ String get boardYourClaimCode => '[Ýöûŕ çļåîɱ çöðé one t]';
+
+ @override
+ String boardTimeLeft(int hours) {
+ return '[$hours ĥ ļéƒţ öñ ýöûŕ çļåîɱ one tw]';
+ }
+
+ @override
+ String get boardClaimCodeHint =>
+ '[Þûţ îţ îñ ţĥé þûļļ ŕéǫûéšţ ðéšçŕîþţîöñ. Öñļý ýöû çåñ šéé îţ. one two three four]';
+
+ @override
+ String get boardClaimCodePlaceholder => '[<ýöûŕ çļåîɱ çöðé> one tw]';
+
+ @override
+ String get boardDraftNotice =>
+ '[Ðŕåƒţ: öñļý åðɱîñš šéé ţĥîš ţåšķ. Îţ îš þûƀļîšĥéð öñçé ţĥé ÅÎ ĥåš ŵŕîţţéñ îţš þŕöɱþţ, öŕ ŵĥéñ ýöû šåṽé ţĥé þŕöɱþţ. one two three four five six seven]';
+
+ @override
+ String get boardPromptWriting =>
+ '[Ŵŕîţîñĝ ţĥé þŕöɱþţ ŵîţĥ ÅÎ… ţĥîš ţåķéš åƀöûţ å ɱîñûţé one two three fo]';
+
+ @override
+ String boardPromptNote(String note) {
+ return '[Ţĥé ÅÎ ðîð ñöţ ŵŕîţé ţĥé þŕöɱþţ: $note one two th]';
+ }
+
+ @override
+ String get boardTaskCancelledNotice =>
+ '[Ţĥîš ţåšķ ŵåš çåñçéļļéð. Îţš îššûéš åŕé öþéñ åĝåîñ. one two three fo]';
+
+ @override
+ String get boardLinkedPosts => '[Ļîñķéð îššûéš one t]';
+
+ @override
+ String get boardNoLinkedPosts =>
+ '[Ñö îššûéš åŕé ļîñķéð ţö ţĥîš ţåšķ. one two th]';
+
+ @override
+ String get boardRemoveFromTask => '[Ŕéɱöṽé ƒŕöɱ ţåšķ one t]';
+
+ @override
+ String get boardAddPosts => '[Åðð îššûéš… one]';
+
+ @override
+ String get boardFieldPostNumbers => '[Κšûé ñûɱƀéŕš one t]';
+
+ @override
+ String get boardHowToBuild => '[Ĥöŵ ţö ƀûîļð îţ one t]';
+
+ @override
+ String get boardStepSetupNumber => '[1. o]';
+
+ @override
+ String get boardStepSetupLink =>
+ '[Šéţ ûþ ýöûŕ ðéṽéļöþɱéñţ éñṽîŕöñɱéñţ one two thr]';
+
+ @override
+ String get boardInstallTools =>
+ '[Îñšţåļļ Ƒļûţţéŕ, Ðåŕţ åñð ţĥé Šéŕṽéŕþöð ÇĻÎ: one two three]';
+
+ @override
+ String get boardInstallGuide => '[Šéŕṽéŕþöð îñšţåļļåţîöñ ĝûîðé one two th]';
+
+ @override
+ String get boardStepPrompt =>
+ '[2. Ĝîṽé ýöûŕ çöðîñĝ åĝéñţ ţĥé þŕöɱþţ one two thr]';
+
+ @override
+ String get boardStepPr => '[3. Öþéñ å þûļļ ŕéǫûéšţ one two]';
+
+ @override
+ String get boardStepPromptLocked =>
+ '[2. Ĝîṽé ýöûŕ çöðîñĝ åĝéñţ ţĥé þŕöɱþţ (çļåîɱ ţĥé ţåšķ ţö ĝéţ ţĥé þŕöɱþţ). one two three four fi]';
+
+ @override
+ String get boardPrRules =>
+ '[Öñé ţåšķ þéŕ þûļļ ŕéǫûéšţ, åĝåîñšţ ɱåîñ. Þûţ ţĥéšé ţŵö ļîñéš îñ îţš ðéšçŕîþţîöñ: one two three four five]';
+
+ @override
+ String get boardPullRequest => '[Þûļļ ŕéǫûéšţ one]';
+
+ @override
+ String boardPrNumber(int number) {
+ return '[Þûļļ ŕéǫûéšţ #$number one t]';
+ }
+
+ @override
+ String boardPrBy(String login) {
+ return '[ƀý $login o]';
+ }
+
+ @override
+ String get boardLatestReview => '[Ļåţéšţ ŕéṽîéŵ one t]';
+
+ @override
+ String get boardActivity => '[Åçţîṽîţý one]';
+
+ @override
+ String get boardNoActivity => '[Ñö åçţîṽîţý ýéţ one t]';
+
+ @override
+ String get boardClaimForPrompt =>
+ '[Çļåîɱ ţĥîš ţåšķ ţö ĝéţ ţĥé þŕöɱþţ. one two th]';
+
+ @override
+ String get boardContributeTitle => '[Ĥöŵ ţö çöñţŕîƀûţé one tw]';
+
+ @override
+ String get boardContributeIntro =>
+ '[Åñýöñé çåñ ƀûîļð å ţåšķ ţĥåţ îš ûþ ƒöŕ ĝŕåƀš. Çļåîɱ îţ, ļéţ å çöðîñĝ åĝéñţ ðö ţĥé ŵöŕķ ŵîţĥ ţĥé ţåšķ\'š þŕöɱþţ, åñð öþéñ å þûļļ ŕéǫûéšţ. one two three four five six seven eigh]';
+
+ @override
+ String get boardContribStep1Title => '[Ĝéţ ţĥé ţööļš one]';
+
+ @override
+ String get boardContribStep1 =>
+ '[Îñšţåļļ Ƒļûţţéŕ, Ðåŕţ åñð ţĥé Šéŕṽéŕþöð ÇĻÎ, åñð åñ ÅÎ çöðîñĝ åĝéñţ ţĥåţ ŕéåðš ÅĜÉÑŢŠ.ɱð, šûçĥ åš Çļåûðé Çöðé. one two three four five six seven]';
+
+ @override
+ String get boardContribStep2Title =>
+ '[Îñšţåļļ öŕ ûþðåţé ţĥé Šéŕṽéŕþöð ÇĻÎ one two thr]';
+
+ @override
+ String get boardContribStep2 =>
+ '[Îţ ŕûñš ţĥé šéŕṽéŕ åñð ţĥé åþþš. Ţĥé šåɱé çöɱɱåñð ûþðåţéš åñ îñšţåļļéð ÇĻÎ ţö ţĥîš ṽéŕšîöñ. Åðð Ðåŕţ\'š îñšţåļļ ðîŕéçţöŕý ţö ýöûŕ ÞÅŢĤ îƒ šéŕṽéŕþöð îš ñöţ ƒöûñð. one two three four five six seven eight nine te]';
+
+ @override
+ String get boardContribStep3Title => '[Ƒöŕķ ţĥé ŕéþöšîţöŕý one tw]';
+
+ @override
+ String get boardContribStep3 =>
+ '[Çŕéåţé ýöûŕ öŵñ çöþý öƒ ţĥé ŕéþöšîţöŕý öñ ĜîţĤûƀ. Ýöû þûšĥ ýöûŕ çĥåñĝéš ţö ýöûŕ ƒöŕķ åñð öþéñ ţĥé þûļļ ŕéǫûéšţ ƒŕöɱ ţĥéŕé. one two three four five six seven e]';
+
+ @override
+ String get boardContribForkLink => '[Ƒöŕķ öñ ĜîţĤûƀ one t]';
+
+ @override
+ String get boardContribStep4Title => '[Çļöñé ţĥé ŕéþöšîţöŕý one two]';
+
+ @override
+ String get boardContribStep4 =>
+ '[Çļöñé ýöûŕ ƒöŕķ ţö ýöûŕ ɱåçĥîñé (ŕéþļåçé <ýöû> ŵîţĥ ýöûŕ ĜîţĤûƀ ûšéŕñåɱé) åñð ĝéţ ţĥé þåçķåĝéš. one two three four five six]';
+
+ @override
+ String get boardContribStep5Title => '[Çöñƒîĝûŕé ţĥé šéŕṽéŕ one two]';
+
+ @override
+ String get boardContribStep5 =>
+ '[Çŕéåţé ýöûŕ þåššŵöŕðš ƒîļé îñ ŵöñķý_šéŕṽéŕ åñð ŕéþļåçé ţĥé ÇĤÅÑĜÉ_ṀÉ ṽåļûéš. ÅÎ ķéýš åŕé öþţîöñåļ. one two three four five six se]';
+
+ @override
+ String get boardContribStep6Title => '[Šţåŕţ ţĥé šéŕṽéŕ one t]';
+
+ @override
+ String get boardContribStep6 =>
+ '[Ŕûñ îţ îñ ŵöñķý_šéŕṽéŕ: îţ šţåŕţš ţĥé ðéṽéļöþɱéñţ ðåţåƀåšé, åþþļîéš ţĥé ɱîĝŕåţîöñš åñð ĥöţ ŕéļöåðš. Šţåŕţ åñ åþþ ƒŕöɱ îţš ÛÎ öŕ ļéţ ýöûŕ åĝéñţ ðö îţ ţĥŕöûĝĥ ţĥé šéŕṽéŕþöð ṀÇÞ šéŕṽéŕ (.ɱçþ.ĵšöñ îš îñ ţĥé ŕéþöšîţöŕý). Šîĝñ ûþ ŵîţĥ åñý éɱåîļ; îñ ðéṽéļöþɱéñţ ţĥé çöðé îš îñ ţĥé šéŕṽéŕ ļöĝ. one two three four five six seven eight nine ten one two three four five six seven]';
+
+ @override
+ String get boardContribStep7Title => '[Þîçķ å ţåšķ one]';
+
+ @override
+ String get boardContribStep7 =>
+ '[Çļåîɱ å ţåšķ ûñðéŕ Ûþ ƒöŕ ĝŕåƀš, þåšţé îţš þŕöɱþţ îñţö ýöûŕ åĝéñţ åñð ŕéṽîéŵ ţĥé ŕéšûļţ. one two three four five si]';
+
+ @override
+ String get boardContribStep8Title => '[Öþéñ å þûļļ ŕéǫûéšţ one tw]';
+
+ @override
+ String get boardContribStep8 =>
+ '[Öñé ţåšķ þéŕ þûļļ ŕéǫûéšţ. Ţĥé ðéšçŕîþţîöñ çöñţåîñš ţĥé Ŵöñķý-Ţåšķ åñð Ŵöñķý-Çļåîɱ ļîñéš ƒŕöɱ ţĥé ţåšķ þåĝé. Þûļļ ŕéǫûéšţš ŵîţĥöûţ å ṽåļîð ţåšķ åŕé çļöšéð åƒţéŕ 7 ðåýš. one two three four five six seven eight nine ten o]';
+
+ @override
+ String get boardContribStep9Title => '[Šîĝñ ţĥé ÇĻÅ åñð ƒöļļöŵ ûþ one two]';
+
+ @override
+ String get boardContribStep9 =>
+ '[΃ ýöû ĥåṽéñ\'ţ šîĝñéð ţĥé Šéŕṽéŕþöð çöñţŕîƀûţöŕ ļîçéñšé åĝŕééɱéñţ (ÇĻÅ) ýéţ, šîĝñ îţ öñ ĜîţĤûƀ: ţĥé ÇĻÅ Åššîšţåñţ ƀöţ åšķš ƒöŕ îţ öñ ýöûŕ þûļļ ŕéǫûéšţ, åñð ýöû ŕéþļý ŵîţĥ ţĥé ļîñé ƀéļöŵ. Ýöû šîĝñ îţ öñçé. Ţĥéñ çĥéçķ ƀåçķ öñçé ýöûŕ þûļļ ŕéǫûéšţ ĥåš ƀééñ ŕéṽîéŵéð, åñð ƒîẋ åñý îššûéš ţĥé ŕéṽîéŵ þöîñţš öûţ. one two three four five six seven eight nine ten one two three four five six seven eight]';
+
+ @override
+ String get boardContribLicense =>
+ '[Çöñţŕîƀûţîöñš åŕé åççéþţéð ûñðéŕ ţĥé ŕéþöšîţöŕý\'š ƁŠÐ-3-Çļåûšé ļîçéñšé. one two three four five]';
+
+ @override
+ String boardContribSecurity(String email) {
+ return '[Ƒöûñð å šéçûŕîţý îššûé? Éɱåîļ $email. Ñéṽéŕ þöšţ îţ öñ ţĥé ƀöåŕð öŕ îñ å þûļļ ŕéǫûéšţ. one two three four five]';
+ }
+
+ @override
+ String get boardContributeGuide => '[Ƒûļļ ĝûîðé öñ ĜîţĤûƀ one tw]';
+
+ @override
+ String get boardFieldTitle => '[Ţîţļé on]';
+
+ @override
+ String get boardTitleLength => '[Ûšé 8 ţö 120 çĥåŕåçţéŕš. one two]';
+
+ @override
+ String get boardModeratorEditHint =>
+ '[Ţĥé îššûé šĥöŵš Éðîţéð ƀý å ɱöðéŕåţöŕ. one two thre]';
+
+ @override
+ String get boardFieldReason => '[Ŕéåšöñ one]';
+
+ @override
+ String get boardFieldNote => '[Ñöţé (öþţîöñåļ) one t]';
+
+ @override
+ String get boardFieldNoteForAuthor =>
+ '[Ṁéššåĝé ţö ţĥé åûţĥöŕ (öþţîöñåļ) one two th]';
+
+ @override
+ String get boardFlagHint =>
+ '[Ƒļåĝĝéð îššûéš åŕé ĥîððéñ ƒöŕ ŕéṽîéŵ åƒţéŕ 3 þéöþļé ƒļåĝ ţĥéɱ. one two three four]';
+
+ @override
+ String get boardFlagAction => '[Ƒļåĝ on]';
+
+ @override
+ String get boardRemoveTitle => '[Ŕéɱöṽé one]';
+
+ @override
+ String get boardDeclineOutOfScope => '[Öûţ öƒ šçöþé one]';
+
+ @override
+ String get boardDeclineAsIntended => '[Ŵöŕķš åš îñţéñðéð one tw]';
+
+ @override
+ String get boardDeclineWontFix => '[Ŵöñ\'ţ ƒîẋ one]';
+
+ @override
+ String boardBlockHint(String name) {
+ return '[$name çåñ šţîļļ ŕéåð ţĥé ƀöåŕð ƀûţ çåñ\'ţ þöšţ, ṽöţé öŕ çöɱɱéñţ. one two three fou]';
+ }
+
+ @override
+ String boardMergeHint(int number) {
+ return '[Ṽöţéš öñ #$number ɱöṽé ţö ţĥé îššûé ţĥåţ ŕéɱåîñš. one two thre]';
+ }
+
+ @override
+ String get boardFieldTargetPost => '[Κšûé ţĥåţ ŕéɱåîñš one tw]';
+
+ @override
+ String get boardInvalidPostNumber => '[Éñţéŕ åñ îššûé ñûɱƀéŕ one two]';
+
+ @override
+ String get boardMergeSelf =>
+ '[Åñ îššûé çåñ\'ţ ƀé ɱéŕĝéð îñţö îţšéļƒ one two thr]';
+
+ @override
+ String get boardFieldKind => '[Ķîñð on]';
+
+ @override
+ String get boardFieldArea => '[Åŕéå on]';
+
+ @override
+ String get boardFieldSize => '[Šîžé on]';
+
+ @override
+ String get boardNoLabel => '[Ñöţ šéţ one]';
+
+ @override
+ String get boardFieldTask => '[Ţåšķ on]';
+
+ @override
+ String get boardLinkPrHint =>
+ '[Å þûļļ ŕéǫûéšţ öƒ ţĥé ŕéþöšîţöŕý. Ļéåṽé îţ éɱþţý ţö ûñļîñķ. one two three four]';
+
+ @override
+ String get boardFieldPrUrl => '[Þûļļ ŕéǫûéšţ ÛŔĻ one t]';
+
+ @override
+ String get boardInvalidPrUrl =>
+ '[Ûšé å ĝîţĥûƀ.çöɱ þûļļ ŕéǫûéšţ ÛŔĻ one two th]';
+
+ @override
+ String get boardRemoveScreenshotHint =>
+ '[Ţĥé šçŕééñšĥöţ îš ðéļéţéð ƒöŕ ĝööð. one two thr]';
+
+ @override
+ String boardPromptEditorTitle(int number) {
+ return '[Þŕöɱþţ öƒ Ţ-$number one]';
+ }
+
+ @override
+ String get boardPromptEditorHint =>
+ '[Çöñţŕîƀûţöŕš ŕûñ ţĥîš þŕöɱþţ îñ çöðîñĝ åĝéñţš. Ŕéåð îţ åļļ ƀéƒöŕé šåṽîñĝ. one two three four fiv]';
+
+ @override
+ String get boardPromptSavePublishes =>
+ '[Šåṽîñĝ þûƀļîšĥéš ţĥé ţåšķ. one two t]';
+
+ @override
+ String get boardPromptDiffTitle => '[Çĥåñĝéš ƒŕöɱ Ŕéĝéñéŕåţé one two]';
+
+ @override
+ String boardLintTitle(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count ûñûšûåļ îñšţŕûçţîöñš',
+ one: '1 ûñûšûåļ îñšţŕûçţîöñ',
+ );
+ return '[$_temp0: çĥéçķ ƀéƒöŕé šåṽîñĝ one two three four f]';
+ }
+
+ @override
+ String boardLintCommand(int line, String text) {
+ return '[Ļîñé $line: çöɱɱåñð $text one t]';
+ }
+
+ @override
+ String boardLintUrl(int line, String text) {
+ return '[Ļîñé $line: ļîñķ öûţšîðé ţĥé ŕéþöšîţöŕý $text one two thr]';
+ }
+
+ @override
+ String boardLintWording(int line, String text) {
+ return '[Ļîñé $line: “$text” one]';
+ }
+
+ @override
+ String get boardReviewHeld => '[Ĥéļð on]';
+
+ @override
+ String get boardReviewCheck => '[Çĥéçķ on]';
+
+ @override
+ String get boardReviewUnchecked => '[Ûñçĥéçķéð one]';
+
+ @override
+ String get boardReviewDuplicates => '[Þöššîƀļé ðûþļîçåţéš one two]';
+
+ @override
+ String get boardReviewBundles => '[Šûĝĝéšţéð ƀûñðļéš one tw]';
+
+ @override
+ String get boardReviewDrafts => '[Ðŕåƒţ ţåšķš one]';
+
+ @override
+ String get boardReviewEmpty => '[Ñöţĥîñĝ ţö ŕéṽîéŵ. one tw]';
+
+ @override
+ String get cmdBoardVote => '[Ṽöţé on]';
+
+ @override
+ String get cmdBoardCreatePost => '[Ŕéþöŕţ îššûé one]';
+
+ @override
+ String get cmdBoardEditPost => '[Éðîţ îššûé one]';
+
+ @override
+ String get cmdBoardDeletePost => '[Ðéļéţé îššûé one]';
+
+ @override
+ String get cmdBoardRemoveScreenshot => '[Ŕéɱöṽé šçŕééñšĥöţ one tw]';
+
+ @override
+ String get cmdBoardComment => '[Çöɱɱéñţ one]';
+
+ @override
+ String get cmdBoardEditComment => '[Éðîţ çöɱɱéñţ one]';
+
+ @override
+ String get cmdBoardDeleteComment => '[Ðéļéţé çöɱɱéñţ one t]';
+
+ @override
+ String get cmdBoardFlag => '[Ƒļåĝ… on]';
+
+ @override
+ String get cmdBoardClaim => '[Çļåîɱ ţåšķ one]';
+
+ @override
+ String get cmdBoardRelease => '[Ûñçļåîɱ ţĥîš ţåšķ one tw]';
+
+ @override
+ String get cmdBoardCopyPrompt => '[Çöþý þŕöɱþţ one]';
+
+ @override
+ String get cmdBoardCopyClaimCode => '[Çöþý çļåîɱ çöðé one t]';
+
+ @override
+ String get cmdBoardModerate => '[Ṁöðéŕåţé one]';
+
+ @override
+ String get cmdBoardSetLabels => '[Šéţ ķîñð, åŕéå åñð šîžé… one two]';
+
+ @override
+ String get cmdBoardAdminEdit => '[Éðîţ åš ɱöðéŕåţöŕ… one tw]';
+
+ @override
+ String get cmdBoardApplyTitle => '[Åþþļý šûĝĝéšţéð ţîţļé one two]';
+
+ @override
+ String get cmdBoardNotDuplicate => '[Ñöţ å ðûþļîçåţé one t]';
+
+ @override
+ String get cmdBoardMerge => '[Ṁéŕĝé îñţö… one]';
+
+ @override
+ String get cmdBoardUnmerge => '[Ûñɱéŕĝé one]';
+
+ @override
+ String get cmdBoardDecline => '[Ðéçļîñé one]';
+
+ @override
+ String get cmdBoardReopen => '[Ŕéöþéñ one]';
+
+ @override
+ String get cmdBoardBlock => '[Ɓļöçķ ûšéŕ ƒŕöɱ ţĥé ƀöåŕð one two]';
+
+ @override
+ String get cmdBoardCreateTask => '[Çŕéåţé ţåšķ one]';
+
+ @override
+ String get cmdBoardEditTask => '[Éðîţ ţåšķ one]';
+
+ @override
+ String get cmdBoardSetTaskPosts => '[Çĥåñĝé ţĥé ţåšķ\'š îššûéš one two]';
+
+ @override
+ String get cmdBoardRegeneratePrompt => '[Ŕéĝéñéŕåţé þŕöɱþţ one tw]';
+
+ @override
+ String get cmdBoardEditPrompt => '[Éðîţ þŕöɱþţ one]';
+
+ @override
+ String get cmdBoardPublishTask => '[Þûƀļîšĥ ţåšķ one]';
+
+ @override
+ String get cmdBoardLinkPr => '[Ļîñķ þûļļ ŕéǫûéšţ… one tw]';
+
+ @override
+ String get cmdBoardMarkDone => '[Ṁåŕķ ðöñé one]';
@override
- String get boardInstallGuide => '[Šéŕṽéŕþöð îñšţåļļåţîöñ ĝûîðé one two th]';
+ String get cmdBoardMarkShipped => '[Ṁåŕķ šĥîþþéð one]';
@override
- String get boardStepPrompt =>
- '[2. Ĝîṽé ýöûŕ çöðîñĝ åĝéñţ ţĥé þŕöɱþţ one two thr]';
+ String get cmdBoardCancelTask => '[Çåñçéļ ţåšķ one]';
@override
- String get boardStepPr => '[3. Öþéñ å þûļļ ŕéǫûéšţ one two]';
+ String get cmdBoardSuggestBundles => '[Šûĝĝéšţ ƀûñðļéš one t]';
@override
- String get boardStepPromptLocked =>
- '[2. Ĝîṽé ýöûŕ çöðîñĝ åĝéñţ ţĥé þŕöɱþţ (çļåîɱ ţĥé ţåšķ ţö ĝéţ ţĥé þŕöɱþţ). one two three four fi]';
+ String get cmdBoardDismissSuggestion => '[Ðîšɱîšš šûĝĝéšţîöñ one tw]';
@override
- String get boardPrRules =>
- '[Öñé ţåšķ þéŕ þûļļ ŕéǫûéšţ, åĝåîñšţ ɱåîñ. Þûţ ţĥéšé ţŵö ļîñéš îñ îţš ðéšçŕîþţîöñ: one two three four five]';
+ String get notifyBoard => '[Ɓûĝš & îɱþŕöṽéɱéñţš one tw]';
@override
- String get boardPullRequest => '[Þûļļ ŕéǫûéšţ one]';
+ String get notifyBoardHint =>
+ '[Ŕéþļîéš ţö ýöûŕ îššûéš, šţåţûš çĥåñĝéš åñð ýöûŕ ţåšķ çļåîɱš one two three four]';
@override
- String boardPrNumber(int number) {
- return '[Þûļļ ŕéǫûéšţ #$number one t]';
+ String boardEventPrClosed(String url) {
+ return '[Þûļļ ŕéǫûéšţ çļöšéð ŵîţĥöûţ ɱéŕĝé: $url. Ţĥé ţåšķ îš öþéñ åĝåîñ. one two three four]';
}
@override
- String boardPrBy(String login) {
- return '[ƀý $login o]';
+ String boardEventPrMerged(String url) {
+ return '[Þûļļ ŕéǫûéšţ ɱéŕĝéð: $url one two]';
}
@override
- String get boardLatestReview => '[Ļåţéšţ ŕéṽîéŵ one t]';
-
- @override
- String get boardActivity => '[Åçţîṽîţý one]';
+ String boardEventPrOpened(String url) {
+ return '[Þûļļ ŕéǫûéšţ öþéñéð: $url one two]';
+ }
@override
- String get boardNoActivity => '[Ñö åçţîṽîţý ýéţ one t]';
+ String boardEventDeployed(String sha) {
+ return '[Šĥîþþéð îñ ðéþļöý $sha one tw]';
+ }
@override
- String get boardClaimForPrompt =>
- '[Çļåîɱ ţĥîš ţåšķ ţö ĝéţ ţĥé þŕöɱþţ. one two th]';
+ String get boardVerdictMet => '[Ṁéţ on]';
@override
- String get boardContributeTitle => '[Ĥöŵ ţö çöñţŕîƀûţé one tw]';
+ String get boardVerdictPartly => '[Þåŕţļý one]';
@override
- String get boardContributeIntro =>
- '[Åñýöñé çåñ ƀûîļð å ţåšķ ţĥåţ îš ûþ ƒöŕ ĝŕåƀš. Çļåîɱ îţ, ļéţ å çöðîñĝ åĝéñţ ðö ţĥé ŵöŕķ ŵîţĥ ţĥé ţåšķ\'š þŕöɱþţ, åñð öþéñ å þûļļ ŕéǫûéšţ. one two three four five six seven eigh]';
+ String get boardVerdictNotMet => '[Ñöţ ɱéţ one]';
@override
- String get boardContribStep1Title => '[Ĝéţ ţĥé ţööļš one]';
+ String get boardVerdictCantTell => '[Çåñ\'ţ ţéļļ one]';
@override
- String get boardContribStep1 =>
- '[Îñšţåļļ Ƒļûţţéŕ, Ðåŕţ åñð ţĥé Šéŕṽéŕþöð ÇĻÎ, åñð åñ ÅÎ çöðîñĝ åĝéñţ ţĥåţ ŕéåðš ÅĜÉÑŢŠ.ɱð, šûçĥ åš Çļåûðé Çöðé. one two three four five six seven]';
+ String get boardVerdictAdded => '[Åððéð on]';
@override
- String get boardContribStep2Title =>
- '[Îñšţåļļ öŕ ûþðåţé ţĥé Šéŕṽéŕþöð ÇĻÎ one two thr]';
+ String get boardVerdictUpdated => '[Ûþðåţéð one]';
@override
- String get boardContribStep2 =>
- '[Îţ ŕûñš ţĥé šéŕṽéŕ åñð ţĥé åþþš. Ţĥé šåɱé çöɱɱåñð ûþðåţéš åñ îñšţåļļéð ÇĻÎ ţö ţĥîš ṽéŕšîöñ. Åðð Ðåŕţ\'š îñšţåļļ ðîŕéçţöŕý ţö ýöûŕ ÞÅŢĤ îƒ šéŕṽéŕþöð îš ñöţ ƒöûñð. one two three four five six seven eight nine te]';
+ String get boardVerdictMissing => '[Ṁîššîñĝ one]';
@override
- String get boardContribStep3Title => '[Ƒöŕķ ţĥé ŕéþöšîţöŕý one tw]';
+ String get boardVerdictNotNeeded => '[Ñöţ ñééðéð one]';
@override
- String get boardContribStep3 =>
- '[Çŕéåţé ýöûŕ öŵñ çöþý öƒ ţĥé ŕéþöšîţöŕý öñ ĜîţĤûƀ. Ýöû þûšĥ ýöûŕ çĥåñĝéš ţö ýöûŕ ƒöŕķ åñð öþéñ ţĥé þûļļ ŕéǫûéšţ ƒŕöɱ ţĥéŕé. one two three four five six seven e]';
+ String get boardReviewTests => '[Ţéšţš on]';
@override
- String get boardContribForkLink => '[Ƒöŕķ öñ ĜîţĤûƀ one t]';
+ String get boardReviewSpecs => '[Šþéçš on]';
@override
- String get boardContribStep4Title => '[Çļöñé ţĥé ŕéþöšîţöŕý one two]';
+ String get boardReviewRisks => '[Ŕîšķš on]';
@override
- String get boardContribStep4 =>
- '[Çļöñé ýöûŕ ƒöŕķ ţö ýöûŕ ɱåçĥîñé (ŕéþļåçé <ýöû> ŵîţĥ ýöûŕ ĜîţĤûƀ ûšéŕñåɱé) åñð ĝéţ ţĥé þåçķåĝéš. one two three four five six]';
+ String get boardReviewUnrelated =>
+ '[Çĥåñĝéš ţĥé ţåšķ ðîðñ\'ţ åšķ ƒöŕ one two th]';
@override
- String get boardContribStep5Title => '[Çöñƒîĝûŕé ţĥé šéŕṽéŕ one two]';
+ String boardReviewedSha(String sha) {
+ return '[Ŕéṽîéŵéð åţ $sha one]';
+ }
@override
- String get boardContribStep5 =>
- '[Çŕéåţé ýöûŕ þåššŵöŕðš ƒîļé îñ ŵöñķý_šéŕṽéŕ åñð ŕéþļåçé ţĥé ÇĤÅÑĜÉ_ṀÉ ṽåļûéš. ÅÎ ķéýš åŕé öþţîöñåļ. one two three four five six se]';
+ String get settingsPublications => '[Þûƀļîçåţîöñš one t]';
@override
- String get boardContribStep6Title => '[Šţåŕţ ţĥé šéŕṽéŕ one t]';
+ String get pubSectionHint =>
+ '[Ļéţ þéöþļé ŵŕîţé ƒöŕ ýöûŕ ƀļöĝ îñ Ŵöñķý Ŵŕîţé åñð šéñð šţöŕîéš ţö ýöûŕ ŕéṽîéŵ ǫûéûé ŵîţĥ öñé ƀûţţöñ. one two three four five six s]';
@override
- String get boardContribStep6 =>
- '[Ŕûñ îţ îñ ŵöñķý_šéŕṽéŕ: îţ šţåŕţš ţĥé ðéṽéļöþɱéñţ ðåţåƀåšé, åþþļîéš ţĥé ɱîĝŕåţîöñš åñð ĥöţ ŕéļöåðš. Šţåŕţ åñ åþþ ƒŕöɱ îţš ÛÎ öŕ ļéţ ýöûŕ åĝéñţ ðö îţ ţĥŕöûĝĥ ţĥé šéŕṽéŕþöð ṀÇÞ šéŕṽéŕ (.ɱçþ.ĵšöñ îš îñ ţĥé ŕéþöšîţöŕý). Šîĝñ ûþ ŵîţĥ åñý éɱåîļ; îñ ðéṽéļöþɱéñţ ţĥé çöðé îš îñ ţĥé šéŕṽéŕ ļöĝ. one two three four five six seven eight nine ten one two three four five six seven]';
+ String get pubNew => '[Ñéŵ þûƀļîçåţîöñ one t]';
@override
- String get boardContribStep7Title => '[Þîçķ å ţåšķ one]';
+ String get pubNewHint =>
+ '[Ŵöñķý šéñðš šûƀɱîţţéð šţöŕîéš ţö ýöûŕ ŵéƀĥööķ. Ýöû ĝéţ ţĥé šîĝñîñĝ šéçŕéţ öñçé, åƒţéŕ ýöû çŕéåţé ţĥé þûƀļîçåţîöñ. one two three four five six seven]';
@override
- String get boardContribStep7 =>
- '[Çļåîɱ å ţåšķ ûñðéŕ Ûþ ƒöŕ ĝŕåƀš, þåšţé îţš þŕöɱþţ îñţö ýöûŕ åĝéñţ åñð ŕéṽîéŵ ţĥé ŕéšûļţ. one two three four five si]';
+ String pubEdit(String name) {
+ return '[Éðîţ “$name” one]';
+ }
@override
- String get boardContribStep8Title => '[Öþéñ å þûļļ ŕéǫûéšţ one tw]';
+ String get pubEditButton => '[Éðîţ… on]';
@override
- String get boardContribStep8 =>
- '[Öñé ţåšķ þéŕ þûļļ ŕéǫûéšţ. Ţĥé ðéšçŕîþţîöñ çöñţåîñš ţĥé Ŵöñķý-Ţåšķ åñð Ŵöñķý-Çļåîɱ ļîñéš ƒŕöɱ ţĥé ţåšķ þåĝé. Þûļļ ŕéǫûéšţš ŵîţĥöûţ å ṽåļîð ţåšķ åŕé çļöšéð åƒţéŕ 7 ðåýš. one two three four five six seven eight nine ten o]';
+ String get pubGuide => '[Ðéṽéļöþéŕ ĝûîðé one t]';
@override
- String get boardContribStep9Title => '[Šîĝñ ţĥé ÇĻÅ åñð ƒöļļöŵ ûþ one two]';
+ String get pubEmpty => '[Ýöû ĥåṽé ñö þûƀļîçåţîöñš ýéţ. one two t]';
@override
- String get boardContribStep9 =>
- '[΃ ýöû ĥåṽéñ\'ţ šîĝñéð ţĥé Šéŕṽéŕþöð çöñţŕîƀûţöŕ ļîçéñšé åĝŕééɱéñţ (ÇĻÅ) ýéţ, šîĝñ îţ öñ ĜîţĤûƀ: ţĥé ÇĻÅ Åššîšţåñţ ƀöţ åšķš ƒöŕ îţ öñ ýöûŕ þûļļ ŕéǫûéšţ, åñð ýöû ŕéþļý ŵîţĥ ţĥé ļîñé ƀéļöŵ. Ýöû šîĝñ îţ öñçé. Ţĥéñ çĥéçķ ƀåçķ öñçé ýöûŕ þûļļ ŕéǫûéšţ ĥåš ƀééñ ŕéṽîéŵéð, åñð ƒîẋ åñý îššûéš ţĥé ŕéṽîéŵ þöîñţš öûţ. one two three four five six seven eight nine ten one two three four five six seven eight]';
+ String get pubName => '[Ñåɱé on]';
@override
- String get boardContribLicense =>
- '[Çöñţŕîƀûţîöñš åŕé åççéþţéð ûñðéŕ ţĥé ŕéþöšîţöŕý\'š ƁŠÐ-3-Çļåûšé ļîçéñšé. one two three four five]';
+ String get pubSiteUrl => '[Šîţé ÛŔĻ one]';
@override
- String boardContribSecurity(String email) {
- return '[Ƒöûñð å šéçûŕîţý îššûé? Éɱåîļ $email. Ñéṽéŕ þöšţ îţ öñ ţĥé ƀöåŕð öŕ îñ å þûļļ ŕéǫûéšţ. one two three four five]';
- }
+ String get pubWebhookUrl => '[Ŵéƀĥööķ ÛŔĻ one]';
@override
- String get boardContributeGuide => '[Ƒûļļ ĝûîðé öñ ĜîţĤûƀ one tw]';
+ String get pubTermsUrl => '[Çöñţŕîƀûţöŕ ţéŕɱš ÛŔĻ (öþţîöñåļ) one two thr]';
@override
- String get boardFieldTitle => '[Ţîţļé on]';
+ String get pubTemplate => '[Ţéɱþļåţé ðöçûɱéñţ (öþţîöñåļ) one two th]';
@override
- String get boardTitleLength => '[Ûšé 8 ţö 120 çĥåŕåçţéŕš. one two]';
+ String get pubTemplateHint =>
+ '[Þåšţé å ļîñķ ţö öñé öƒ ýöûŕ Ŵöñķý Ŵŕîţé ðöçûɱéñţš one two three]';
@override
- String get boardModeratorEditHint =>
- '[Ţĥé îššûé šĥöŵš Éðîţéð ƀý å ɱöðéŕåţöŕ. one two thre]';
+ String get pubTemplateInvalid =>
+ '[Þåšţé ţĥé ļîñķ öƒ å Ŵöñķý Ŵŕîţé ðöçûɱéñţ. one two thre]';
@override
- String get boardFieldReason => '[Ŕéåšöñ one]';
+ String get pubRequired => '[Ŕéǫûîŕéð. one]';
@override
- String get boardFieldNote => '[Ñöţé (öþţîöñåļ) one t]';
+ String get pubHttpsOnly => '[Ûšé åñ ĥţţþš:// åððŕéšš. one two]';
@override
- String get boardFieldNoteForAuthor =>
- '[Ṁéššåĝé ţö ţĥé åûţĥöŕ (öþţîöñåļ) one two th]';
+ String get pubFieldSummary => '[Šûɱɱåŕý one]';
@override
- String get boardFlagHint =>
- '[Ƒļåĝĝéð îššûéš åŕé ĥîððéñ ƒöŕ ŕéṽîéŵ åƒţéŕ 3 þéöþļé ƒļåĝ ţĥéɱ. one two three four]';
+ String get pubFieldTags => '[Ţåĝš on]';
@override
- String get boardFlagAction => '[Ƒļåĝ on]';
+ String get pubAllowedTags => '[Åļļöŵéð ţåĝš (öþţîöñåļ) one two]';
@override
- String get boardRemoveTitle => '[Ŕéɱöṽé one]';
+ String get pubAllowedTagsHint =>
+ '[Çöɱɱå šéþåŕåţéð; éɱþţý åļļöŵš åñý ţåĝ one two thre]';
@override
- String get boardDeclineOutOfScope => '[Öûţ öƒ šçöþé one]';
+ String get pubFieldCover => '[Çöṽéŕ îɱåĝé one]';
@override
- String get boardDeclineAsIntended => '[Ŵöŕķš åš îñţéñðéð one tw]';
+ String get pubFieldBio => '[Åûţĥöŕ ƀîö one]';
@override
- String get boardDeclineWontFix => '[Ŵöñ\'ţ ƒîẋ one]';
+ String get pubFieldNote => '[Ñöţé ţö ţĥé ɱöðéŕåţöŕš one two]';
@override
- String boardBlockHint(String name) {
- return '[$name çåñ šţîļļ ŕéåð ţĥé ƀöåŕð ƀûţ çåñ\'ţ þöšţ, ṽöţé öŕ çöɱɱéñţ. one two three fou]';
- }
+ String get pubFieldOff => '[Ñöţ åšķéð one]';
@override
- String boardMergeHint(int number) {
- return '[Ṽöţéš öñ #$number ɱöṽé ţö ţĥé îššûé ţĥåţ ŕéɱåîñš. one two thre]';
- }
+ String get pubFieldOptional => '[Öþţîöñåļ one]';
@override
- String get boardFieldTargetPost => '[Κšûé ţĥåţ ŕéɱåîñš one tw]';
+ String get pubFieldRequired => '[Ŕéǫûîŕéð one]';
@override
- String get boardInvalidPostNumber => '[Éñţéŕ åñ îššûé ñûɱƀéŕ one two]';
+ String get pubEnabled => '[Åççéþţ ñéŵ šţöŕîéš one tw]';
@override
- String get boardMergeSelf =>
- '[Åñ îššûé çåñ\'ţ ƀé ɱéŕĝéð îñţö îţšéļƒ one two thr]';
+ String pubHostVerified(String host) {
+ return '[$host ✓ o]';
+ }
@override
- String get boardFieldKind => '[Ķîñð on]';
+ String pubHostUnverified(String host) {
+ return '[$host (ñöţ ṽéŕîƒîéð) one t]';
+ }
@override
- String get boardFieldArea => '[Åŕéå on]';
+ String get pubDisabled => '[Ñöţ åççéþţîñĝ šţöŕîéš one two]';
@override
- String get boardFieldSize => '[Šîžé on]';
+ String pubOpenCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '$count šţöŕîéš îñ ŕéṽîéŵ',
+ one: '1 šţöŕý îñ ŕéṽîéŵ',
+ zero: 'Ñö šţöŕîéš îñ ŕéṽîéŵ',
+ );
+ return '[$_temp0 one two three fou]';
+ }
@override
- String get boardNoLabel => '[Ñöţ šéţ one]';
+ String get pubManage => '[Ṁåñåĝé one]';
@override
- String get boardFieldTask => '[Ţåšķ on]';
+ String get pubHide => '[Ĥîðé on]';
@override
- String get boardLinkPrHint =>
- '[Å þûļļ ŕéǫûéšţ öƒ ţĥé ŕéþöšîţöŕý. Ļéåṽé îţ éɱþţý ţö ûñļîñķ. one two three four]';
+ String get pubContributorLink =>
+ '[Çöñţŕîƀûţöŕ ļîñķ (þûţ îţ ƀéĥîñð “Ŵŕîţé ƒöŕ ûš”) one two three]';
@override
- String get boardFieldPrUrl => '[Þûļļ ŕéǫûéšţ ÛŔĻ one t]';
+ String get pubId => '[Þûƀļîçåţîöñ îð one t]';
@override
- String get boardInvalidPrUrl =>
- '[Ûšé å ĝîţĥûƀ.çöɱ þûļļ ŕéǫûéšţ ÛŔĻ one two th]';
+ String get pubCopy => '[Çöþý on]';
@override
- String get boardRemoveScreenshotHint =>
- '[Ţĥé šçŕééñšĥöţ îš ðéļéţéð ƒöŕ ĝööð. one two thr]';
+ String get pubCopied => '[Çöþîéð one]';
@override
- String boardPromptEditorTitle(int number) {
- return '[Þŕöɱþţ öƒ Ţ-$number one]';
- }
+ String get pubDelete => '[Ðéļéţé one]';
@override
- String get boardPromptEditorHint =>
- '[Çöñţŕîƀûţöŕš ŕûñ ţĥîš þŕöɱþţ îñ çöðîñĝ åĝéñţš. Ŕéåð îţ åļļ ƀéƒöŕé šåṽîñĝ. one two three four fiv]';
+ String get pubModerators => '[Ṁöðéŕåţöŕš one]';
@override
- String get boardPromptSavePublishes =>
- '[Šåṽîñĝ þûƀļîšĥéš ţĥé ţåšķ. one two t]';
+ String get pubModeratorsHint =>
+ '[Ṁöðéŕåţöŕš ĝéţ çöɱɱéñţ åççéšš ţö šţöŕîéš ŵĥéñ ţĥéý åŕé šûƀɱîţţéð. Éñţéŕ ţĥé éɱåîļ åððŕéššéš öƒ ţĥéîŕ Ŵöñķý åççöûñţš, öñé þéŕ ļîñé. one two three four five six seven eight]';
@override
- String get boardPromptDiffTitle => '[Çĥåñĝéš ƒŕöɱ Ŕéĝéñéŕåţé one two]';
+ String get pubModeratorEmails => '[Éɱåîļ åððŕéššéš one t]';
@override
- String boardLintTitle(int count) {
+ String pubModeratorsButton(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
- other: '$count ûñûšûåļ îñšţŕûçţîöñš',
- one: '1 ûñûšûåļ îñšţŕûçţîöñ',
+ other: '$count ɱöðéŕåţöŕš…',
+ one: '1 ɱöðéŕåţöŕ…',
+ zero: 'Åðð ɱöðéŕåţöŕš…',
);
- return '[$_temp0: çĥéçķ ƀéƒöŕé šåṽîñĝ one two three four f]';
+ return '[$_temp0 one two three]';
}
@override
- String boardLintCommand(int line, String text) {
- return '[Ļîñé $line: çöɱɱåñð $text one t]';
- }
+ String get pubVerification => '[Ṽéŕîƒîçåţîöñ one t]';
@override
- String boardLintUrl(int line, String text) {
- return '[Ļîñé $line: ļîñķ öûţšîðé ţĥé ŕéþöšîţöŕý $text one two thr]';
+ String pubVerifyHint(String host) {
+ return '[Šéŕṽé ţĥîš åţ ĥţţþš://$host/.ŵéļļ-ķñöŵñ/ŵöñķý-þûƀļîçåţîöñš.ĵšöñ, ţĥéñ ṽéŕîƒý. Ûñţîļ ţĥéñ çöñţŕîƀûţöŕš šéé ýöûŕ ŵéƀĥööķ\'š ĥöšţ åñð å ŵåŕñîñĝ. one two three four five six seven eight ni]';
}
@override
- String boardLintWording(int line, String text) {
- return '[Ļîñé $line: “$text” one]';
+ String pubVerifiedAt(String host, String date) {
+ return '[$host ṽéŕîƒîéð $date one]';
}
@override
- String get boardReviewHeld => '[Ĥéļð on]';
+ String get pubVerify => '[Ṽéŕîƒý one]';
@override
- String get boardReviewCheck => '[Çĥéçķ on]';
+ String pubVerified(String host) {
+ return '[$host îš ṽéŕîƒîéð one]';
+ }
@override
- String get boardReviewUnchecked => '[Ûñçĥéçķéð one]';
+ String get pubVerifyFailed => '[Ñöţ ṽéŕîƒîéð one]';
@override
- String get boardReviewDuplicates => '[Þöššîƀļé ðûþļîçåţéš one two]';
+ String get pubCredentials => '[Šîĝñîñĝ šéçŕéţ åñð ÅÞÎ ķéý one two]';
@override
- String get boardReviewBundles => '[Šûĝĝéšţéð ƀûñðļéš one tw]';
+ String get pubSecretHint =>
+ '[Ŵéƀĥööķš åŕé šîĝñéð ŵîţĥ ţĥé šîĝñîñĝ šéçŕéţ. Ýöûŕ šéŕṽéŕ çåļļš ţĥé þûƀļîçåţîöñ ÅÞÎ ŵîţĥ ţĥé ÅÞÎ ķéý. one two three four five six se]';
@override
- String get boardReviewDrafts => '[Ðŕåƒţ ţåšķš one]';
+ String pubRotationActive(String date) {
+ return '[Ţĥé þŕéṽîöûš šîĝñîñĝ šéçŕéţ šţîļļ šîĝñš ûñţîļ $date. one two three]';
+ }
@override
- String get boardReviewEmpty => '[Ñöţĥîñĝ ţö ŕéṽîéŵ. one tw]';
+ String get pubRotateSecret => '[Ŕöţåţé šîĝñîñĝ šéçŕéţ one two]';
@override
- String get cmdBoardVote => '[Ṽöţé on]';
+ String get pubRotateSecretBody =>
+ '[Ýöû ĝéţ å ñéŵ šéçŕéţ. Ƒöŕ 24 ĥöûŕš, ŵéƀĥööķš çåŕŕý šîĝñåţûŕéš öƒ ţĥé öļð åñð ţĥé ñéŵ šéçŕéţ. one two three four five six]';
@override
- String get cmdBoardCreatePost => '[Ŕéþöŕţ îššûé one]';
+ String get pubRotate => '[Ŕöţåţé one]';
@override
- String get cmdBoardEditPost => '[Éðîţ îššûé one]';
+ String get pubCreateApiKey => '[Çŕéåţé ÅÞÎ ķéý one t]';
@override
- String get cmdBoardDeletePost => '[Ðéļéţé îššûé one]';
+ String get pubRotateApiKey => '[Ŕöţåţé ÅÞÎ ķéý one t]';
@override
- String get cmdBoardRemoveScreenshot => '[Ŕéɱöṽé šçŕééñšĥöţ one tw]';
+ String pubRotateApiKeyLast4(String last4) {
+ return '[Ŕöţåţé ÅÞÎ ķéý (…$last4) one tw]';
+ }
@override
- String get cmdBoardComment => '[Çöɱɱéñţ one]';
+ String get pubRotateApiKeyBody =>
+ '[Ţĥé çûŕŕéñţ ķéý šţöþš ŵöŕķîñĝ åţ öñçé. one two thre]';
@override
- String get cmdBoardEditComment => '[Éðîţ çöɱɱéñţ one]';
+ String get pubApiKey => '[ÅÞÎ ķéý one]';
@override
- String get cmdBoardDeleteComment => '[Ðéļéţé çöɱɱéñţ one t]';
+ String get pubSigningSecret => '[Šîĝñîñĝ šéçŕéţ one t]';
@override
- String get cmdBoardFlag => '[Ƒļåĝ… on]';
+ String pubSecretOnce(String what) {
+ return '[Çöþý ţĥé $what ñöŵ. Îţ îšñ\'ţ šĥöŵñ åĝåîñ. one two thr]';
+ }
@override
- String get cmdBoardClaim => '[Çļåîɱ ţåšķ one]';
+ String get pubSecretSaved => '[Î\'ṽé šţöŕéð îţ one t]';
@override
- String get cmdBoardRelease => '[Ûñçļåîɱ ţĥîš ţåšķ one tw]';
+ String get pubDeliveries => '[Ðéļîṽéŕîéš one]';
@override
- String get cmdBoardCopyPrompt => '[Çöþý þŕöɱþţ one]';
+ String get pubSendTest => '[Šéñð ţéšţ one]';
@override
- String get cmdBoardCopyClaimCode => '[Çöþý çļåîɱ çöðé one t]';
+ String get pubTestOk => '[Ýöûŕ ŵéƀĥööķ åççéþţéð ţĥé ţéšţ. one two th]';
@override
- String get cmdBoardModerate => '[Ṁöðéŕåţé one]';
+ String get pubTestFailed =>
+ '[Ýöûŕ ŵéƀĥööķ ðîðñ\'ţ åççéþţ ţĥé ţéšţ one two thr]';
@override
- String get cmdBoardSetLabels => '[Šéţ ķîñð, åŕéå åñð šîžé… one two]';
+ String get pubNoDeliveries => '[Ñö ŵéƀĥööķš šéñţ ýéţ. one two]';
@override
- String get cmdBoardAdminEdit => '[Éðîţ åš ɱöðéŕåţöŕ… one tw]';
+ String pubDelivered(int status_code) {
+ return '[Ðéļîṽéŕéð (ĤŢŢÞ $status_code) one tw]';
+ }
@override
- String get cmdBoardApplyTitle => '[Åþþļý šûĝĝéšţéð ţîţļé one two]';
+ String pubDeliveryFailed(String error) {
+ return '[Ƒåîļéð: $error one]';
+ }
@override
- String get cmdBoardNotDuplicate => '[Ñöţ å ðûþļîçåţé one t]';
+ String get pubDeliveryQueued => '[Ǫûéûéð one]';
@override
- String get cmdBoardMerge => '[Ṁéŕĝé îñţö… one]';
+ String pubDeliveryRetrying(String error, String date) {
+ return '[$error · ñéẋţ ţŕý $date one]';
+ }
@override
- String get cmdBoardUnmerge => '[Ûñɱéŕĝé one]';
+ String get pubResend => '[Ŕéšéñð one]';
@override
- String get cmdBoardDecline => '[Ðéçļîñé one]';
+ String pubDeleteTitle(String name) {
+ return '[Ðéļéţé “$name”? one]';
+ }
@override
- String get cmdBoardReopen => '[Ŕéöþéñ one]';
+ String get pubDeleteBody =>
+ '[Çöñţŕîƀûţöŕš\' ðöçûɱéñţš šţåý îñ ţĥéîŕ ðŕîṽéš, ƀûţ ţĥéý çåñ\'ţ šûƀɱîţ ţĥéɱ åñýɱöŕé, åñð ɱöðéŕåţöŕš ļöšé åççéšš. Ýöûŕ ƀļöĝ ĝéţš ñö ŵéƀĥööķ åƀöûţ îţ. one two three four five six seven eight nine]';
@override
- String get cmdBoardBlock => '[Ɓļöçķ ûšéŕ ƒŕöɱ ţĥé ƀöåŕð one two]';
+ String get notifySubmissions => '[Šţöŕîéš ƒöŕ þûƀļîçåţîöñš one two]';
@override
- String get cmdBoardCreateTask => '[Çŕéåţé ţåšķ one]';
+ String get notifySubmissionsHint =>
+ '[Šţåţûš çĥåñĝéš åñð ɱéššåĝéš ƒŕöɱ ƀļöĝš ýöû ŵŕîţé ƒöŕ one two three fo]';
@override
- String get cmdBoardEditTask => '[Éðîţ ţåšķ one]';
+ String notifSubmissionChanges(String publication, String what) {
+ return '[$publication åšķéð ƒöŕ çĥåñĝéš ţö “$what” one two]';
+ }
@override
- String get cmdBoardSetTaskPosts => '[Çĥåñĝé ţĥé ţåšķ\'š îššûéš one two]';
+ String notifSubmissionDeclined(String publication, String what) {
+ return '[$publication ðéçļîñéð “$what” one]';
+ }
@override
- String get cmdBoardRegeneratePrompt => '[Ŕéĝéñéŕåţé þŕöɱþţ one tw]';
+ String notifSubmissionPublished(String publication, String what) {
+ return '[$publication þûƀļîšĥéð “$what” one]';
+ }
@override
- String get cmdBoardEditPrompt => '[Éðîţ þŕöɱþţ one]';
+ String notifSubmissionInReview(String publication, String what) {
+ return '[$publication îš ŕéṽîéŵîñĝ “$what” one t]';
+ }
@override
- String get cmdBoardPublishTask => '[Þûƀļîšĥ ţåšķ one]';
+ String notifSubmissionMessage(String publication, String what) {
+ return '[$publication šéñţ å ɱéššåĝé åƀöûţ “$what” one two]';
+ }
@override
- String get cmdBoardLinkPr => '[Ļîñķ þûļļ ŕéǫûéšţ… one tw]';
+ String notifSubmissionFailed(String publication, String what) {
+ return '[$publication çöûļðñ\'ţ ŕéçéîṽé “$what” one tw]';
+ }
@override
- String get cmdBoardMarkDone => '[Ṁåŕķ ðöñé one]';
+ String shareModerator(String publication) {
+ return '[Ṁöðéŕåţöŕ · $publication one]';
+ }
@override
- String get cmdBoardMarkShipped => '[Ṁåŕķ šĥîþþéð one]';
+ String activitySubmitted(String who, String publication) {
+ return '[$who šûƀɱîţţéð îţ ţö $publication one t]';
+ }
@override
- String get cmdBoardCancelTask => '[Çåñçéļ ţåšķ one]';
+ String activitySubmissionWithdrawn(String who, String publication) {
+ return '[$who ŵîţĥðŕéŵ îţ ƒŕöɱ $publication one t]';
+ }
@override
- String get cmdBoardSuggestBundles => '[Šûĝĝéšţ ƀûñðļéš one t]';
+ String activitySubmissionDisconnected(String who, String publication) {
+ return '[$who ðîšçöññéçţéð îţ ƒŕöɱ $publication one two]';
+ }
@override
- String get cmdBoardDismissSuggestion => '[Ðîšɱîšš šûĝĝéšţîöñ one tw]';
+ String activitySubmissionReconnected(String who, String publication) {
+ return '[$who çöññéçţéð îţ ţö $publication åĝåîñ one two]';
+ }
@override
- String get notifyBoard => '[Ɓûĝš & îɱþŕöṽéɱéñţš one tw]';
+ String activitySubmissionStatus(String publication, String status) {
+ return '[$publication: $status o]';
+ }
@override
- String get notifyBoardHint =>
- '[Ŕéþļîéš ţö ýöûŕ îššûéš, šţåţûš çĥåñĝéš åñð ýöûŕ ţåšķ çļåîɱš one two three four]';
+ String get submissionStatusDraft => '[Ðŕåƒţ on]';
@override
- String boardEventPrClosed(String url) {
- return '[Þûļļ ŕéǫûéšţ çļöšéð ŵîţĥöûţ ɱéŕĝé: $url. Ţĥé ţåšķ îš öþéñ åĝåîñ. one two three four]';
- }
+ String get submissionStatusInReview => '[Îñ ŕéṽîéŵ one]';
@override
- String boardEventPrMerged(String url) {
- return '[Þûļļ ŕéǫûéšţ ɱéŕĝéð: $url one two]';
- }
+ String get submissionStatusChangesRequested => '[Çĥåñĝéš ŕéǫûéšţéð one tw]';
@override
- String boardEventPrOpened(String url) {
- return '[Þûļļ ŕéǫûéšţ öþéñéð: $url one two]';
- }
+ String get submissionStatusDeclined => '[Ðéçļîñéð one]';
@override
- String boardEventDeployed(String sha) {
- return '[Šĥîþþéð îñ ðéþļöý $sha one tw]';
- }
+ String get submissionStatusPublished => '[Þûƀļîšĥéð one]';
@override
- String get boardVerdictMet => '[Ṁéţ on]';
+ String get submissionStatusWithdrawn => '[Ŵîţĥðŕåŵñ one]';
@override
- String get boardVerdictPartly => '[Þåŕţļý one]';
+ String pubStartTitle(String name) {
+ return '[Ŵŕîţé ƒöŕ $name one]';
+ }
@override
- String get boardVerdictNotMet => '[Ñöţ ɱéţ one]';
+ String pubStartHostVerified(String host) {
+ return '[$host ✓ o]';
+ }
@override
- String get boardVerdictCantTell => '[Çåñ\'ţ ţéļļ one]';
+ String pubStartHostUnverified(String site, String webhook) {
+ return '[Ŵöñķý ĥåšñ\'ţ ṽéŕîƒîéð ţĥåţ ţĥîš þûƀļîçåţîöñ ƀéļöñĝš ţö $site. Ýöûŕ šţöŕý îš šéñţ ţö $webhook. one two three four five]';
+ }
@override
- String get boardVerdictAdded => '[Åððéð on]';
+ String pubStartConsent(String name) {
+ return '[Ýöû\'ļļ ŵŕîţé îñ ýöûŕ öŵñ Ŵöñķý ðöçûɱéñţ. Ŵĥéñ ýöû þŕéšš Šûƀɱîţ, $name ŕéçéîṽéš ţĥé ţéẋţ åñð îɱåĝéš öƒ ţĥé ðöçûɱéñţ, ţĥé ƒîéļðš ýöû ƒîļļ îñ åñð ýöûŕ ðîšþļåý ñåɱé, åñð îţš ɱöðéŕåţöŕš çåñ ŕéåð åñð çöɱɱéñţ öñ ţĥé ðöçûɱéñţ. one two three four five six seven eight nine ten one two three]';
+ }
@override
- String get boardVerdictUpdated => '[Ûþðåţéð one]';
+ String get pubStartWhich => '[Šţöŕý on]';
@override
- String get boardVerdictMissing => '[Ṁîššîñĝ one]';
+ String pubStartContinue(String name) {
+ return '[Çöñţîñûé “$name” one]';
+ }
@override
- String get boardVerdictNotNeeded => '[Ñöţ ñééðéð one]';
+ String get pubStartNew => '[Šţåŕţ å ñéŵ šţöŕý one t]';
@override
- String get boardReviewTests => '[Ţéšţš on]';
+ String pubStartTerms(String name, String url) {
+ return '[Î åĝŕéé ţö $name\'š çöñţŕîƀûţöŕ ţéŕɱš ($url) one two th]';
+ }
@override
- String get boardReviewSpecs => '[Šþéçš on]';
+ String pubStartTermsRequired(String name) {
+ return '[Åĝŕéé ţö $name\'š çöñţŕîƀûţöŕ ţéŕɱš ţö šţåŕţ. one two thre]';
+ }
@override
- String get boardReviewRisks => '[Ŕîšķš on]';
+ String get pubStartButton => '[Šţåŕţ ŵŕîţîñĝ one t]';
@override
- String get boardReviewUnrelated =>
- '[Çĥåñĝéš ţĥé ţåšķ ðîðñ\'ţ åšķ ƒöŕ one two th]';
+ String pubStartBlocked(String name) {
+ return '[$name îšñ\'ţ åççéþţîñĝ šţöŕîéš ƒŕöɱ ýöû. one two thr]';
+ }
@override
- String boardReviewedSha(String sha) {
- return '[Ŕéṽîéŵéð åţ $sha one]';
+ String pubStartClosed(String name) {
+ return '[$name îšñ\'ţ åççéþţîñĝ šţöŕîéš ŕîĝĥţ ñöŵ. one two thr]';
}
}
diff --git a/packages/wonky_app/lib/src/notifications/notifications_button.dart b/packages/wonky_app/lib/src/notifications/notifications_button.dart
index 40902955..58d0b89f 100644
--- a/packages/wonky_app/lib/src/notifications/notifications_button.dart
+++ b/packages/wonky_app/lib/src/notifications/notifications_button.dart
@@ -461,6 +461,25 @@ String notificationText(WonkyAppLocalizations l, NotificationView n) {
final String m when m.isNotEmpty => m,
_ => l.notifBoardUpdate('${_payload(n)['title'] ?? ''}'),
},
+ NotificationKind.submission => _submissionText(l, n, what),
+ };
+}
+
+/// (M14) A publication's update on a story (specs/18 §5.3).
+String _submissionText(
+ WonkyAppLocalizations l,
+ NotificationView n,
+ String what,
+) {
+ final p = _payload(n);
+ final pub = '${p['publication'] ?? l.someone}';
+ return switch ((p['event'], p['status'])) {
+ ('deliveryFailed', _) => l.notifSubmissionFailed(pub, what),
+ ('status', 'changesRequested') => l.notifSubmissionChanges(pub, what),
+ ('status', 'declined') => l.notifSubmissionDeclined(pub, what),
+ ('status', 'published') => l.notifSubmissionPublished(pub, what),
+ ('status', 'inReview') => l.notifSubmissionInReview(pub, what),
+ _ => l.notifSubmissionMessage(pub, what),
};
}
@@ -496,6 +515,7 @@ IconData _kindIcon(NotificationKind k) => switch (k) {
NotificationKind.boardReply => LucideIcons.messageSquareReply,
NotificationKind.boardStatus => LucideIcons.bug,
NotificationKind.boardClaim => LucideIcons.hand,
+ NotificationKind.submission => LucideIcons.newspaper,
};
/// One notification row: avatar with the kind icon, text, excerpt, time and
diff --git a/packages/wonky_app/lib/src/publications/publication_repository.dart b/packages/wonky_app/lib/src/publications/publication_repository.dart
new file mode 100644
index 00000000..e94741b8
--- /dev/null
+++ b/packages/wonky_app/lib/src/publications/publication_repository.dart
@@ -0,0 +1,129 @@
+import 'package:wonky_client/wonky_client.dart';
+
+import '../data/api.dart';
+
+/// Publications and submissions (specs/18 §9): starting a story from a
+/// contributor link, a document's submission, and the owner's settings.
+abstract class PublicationRepository {
+ // Contributor.
+ Future resolve(String publicId);
+ Future startSubmission(
+ String publicId, {
+ String? ref,
+ required bool agreed,
+ });
+
+ // A document's submission.
+ Future submission(UuidValue itemUuid);
+ Future submit(
+ UuidValue itemUuid,
+ SubmissionValues values, {
+ required bool agreed,
+ });
+ Future withdraw(UuidValue itemUuid);
+ Future disconnect(UuidValue itemUuid);
+ Future reconnect(UuidValue itemUuid);
+
+ // Owner.
+ Future> list();
+ Future create(PublicationInput input);
+ Future update(String publicId, PublicationInput input);
+ Future delete(String publicId);
+ Future verify(String publicId);
+ Future rotateSecret(String publicId);
+ Future createApiKey(String publicId);
+ Future setModerators(
+ String publicId,
+ List emails,
+ );
+ Future> deliveries(String publicId);
+ Future resend(String publicId, String webhookId);
+ Future sendTest(String publicId);
+}
+
+/// [PublicationRepository] on the `publication` and `submission` endpoints.
+class ServerPublicationRepository implements PublicationRepository {
+ ServerPublicationRepository(this.api);
+
+ final Api api;
+
+ @override
+ Future resolve(String publicId) =>
+ api.guard((c) => c.publication.resolve(publicId));
+
+ @override
+ Future startSubmission(
+ String publicId, {
+ String? ref,
+ required bool agreed,
+ }) => api.guard((c) => c.publication.startSubmission(publicId, ref, agreed));
+
+ @override
+ Future submission(UuidValue itemUuid) =>
+ api.guard((c) => c.submission.get(itemUuid));
+
+ @override
+ Future submit(
+ UuidValue itemUuid,
+ SubmissionValues values, {
+ required bool agreed,
+ }) => api.guard((c) => c.submission.submit(itemUuid, values, agreed));
+
+ @override
+ Future withdraw(UuidValue itemUuid) =>
+ api.guard((c) => c.submission.withdraw(itemUuid));
+
+ @override
+ Future disconnect(UuidValue itemUuid) =>
+ api.guard((c) => c.submission.disconnect(itemUuid));
+
+ @override
+ Future reconnect(UuidValue itemUuid) =>
+ api.guard((c) => c.submission.reconnect(itemUuid));
+
+ @override
+ Future> list() =>
+ api.guard((c) => c.publication.list());
+
+ @override
+ Future create(PublicationInput input) =>
+ api.guard((c) => c.publication.create(input));
+
+ @override
+ Future update(String publicId, PublicationInput input) =>
+ api.guard((c) => c.publication.update(publicId, input));
+
+ @override
+ Future delete(String publicId) =>
+ api.guard((c) => c.publication.delete(publicId));
+
+ @override
+ Future verify(String publicId) =>
+ api.guard((c) => c.publication.verify(publicId));
+
+ @override
+ Future rotateSecret(String publicId) =>
+ api.guard((c) => c.publication.rotateSecret(publicId));
+
+ @override
+ Future createApiKey(String publicId) =>
+ api.guard((c) => c.publication.createApiKey(publicId));
+
+ @override
+ Future setModerators(
+ String publicId,
+ List emails,
+ ) => api.guard((c) => c.publication.setModerators(publicId, emails));
+
+ @override
+ Future> deliveries(String publicId) =>
+ api.guard((c) => c.publication.deliveries(publicId));
+
+ @override
+ Future resend(String publicId, String webhookId) =>
+ api.guard((c) => c.publication.resend(publicId, webhookId));
+
+ @override
+ Future sendTest(String publicId) =>
+ api.guard((c) => c.publication.sendTest(publicId));
+}
diff --git a/packages/wonky_app/lib/src/publications/publications_section.dart b/packages/wonky_app/lib/src/publications/publications_section.dart
new file mode 100644
index 00000000..005fcda9
--- /dev/null
+++ b/packages/wonky_app/lib/src/publications/publications_section.dart
@@ -0,0 +1,753 @@
+import 'package:flutter/services.dart';
+import 'package:flutter/widgets.dart';
+import 'package:intl/intl.dart' as intl;
+import 'package:shad/shad.dart';
+import 'package:wonky_client/wonky_client.dart';
+
+import '../bootstrap/app_urls.dart';
+import '../data/api.dart';
+import '../l10n/wonky_app_localizations.dart';
+import '../settings/settings_screen.dart';
+import '../shell/dialogs.dart';
+import '../shell/form_dialog.dart';
+import '../shell/shell_services.dart';
+import 'publication_repository.dart';
+
+/// The developer guide for blog owners (specs/18 §12 PUB-7).
+const publicationGuideUrl =
+ 'https://github.com/serverpod/wonky/blob/main/docs/blog-integration.md';
+
+/// Wonky Work settings › Publications (specs/18 §8.1): the user's
+/// publications, their credentials, moderators, verification and webhook
+/// deliveries.
+class PublicationsSection extends StatefulWidget {
+ const PublicationsSection({super.key, required this.services});
+
+ final ShellServices services;
+
+ @override
+ State createState() => _PublicationsSectionState();
+}
+
+class _PublicationsSectionState extends State {
+ List? _list;
+ Object? _loadError;
+
+ /// The secret shown once: (publicId, label, value).
+ (String, String, String)? _secret;
+
+ /// The publication whose details are open.
+ String? _open;
+ List? _deliveries;
+
+ ShellServices get s => widget.services;
+ PublicationRepository get repo => s.repositories.publications;
+
+ @override
+ void initState() {
+ super.initState();
+ _reload();
+ }
+
+ Future _reload() async {
+ try {
+ final list = await repo.list();
+ if (mounted) {
+ setState(() {
+ _list = list;
+ _loadError = null;
+ });
+ }
+ } catch (e) {
+ if (mounted) setState(() => _loadError = e);
+ }
+ }
+
+ void _replace(PublicationSettings p) => setState(() {
+ _list = [
+ for (final x in _list ?? const [])
+ x.publicId == p.publicId ? p : x,
+ ];
+ });
+
+ Future _guard(Future Function() run) async {
+ try {
+ return await run();
+ } catch (e) {
+ if (mounted) {
+ final l = WonkyAppLocalizations.of(context);
+ s.toasts.error(l.errorGeneric, description: errorMessage(e));
+ }
+ return null;
+ }
+ }
+
+ void _copy(String text) {
+ Clipboard.setData(ClipboardData(text: text));
+ s.toasts.show(WonkyAppLocalizations.of(context).pubCopied);
+ }
+
+ // ------------------------------------------------------------ editing
+
+ Future _edit([PublicationSettings? p]) async {
+ final l = WonkyAppLocalizations.of(context);
+ final f = p?.fields;
+ String? required(String v) => v.trim().isEmpty ? l.pubRequired : null;
+ String? https(String v) {
+ final t = v.trim();
+ if (t.isEmpty) return null;
+ return t.startsWith('https://') ? null : l.pubHttpsOnly;
+ }
+
+ Map modes() => {
+ 'off': l.pubFieldOff,
+ 'optional': l.pubFieldOptional,
+ 'required': l.pubFieldRequired,
+ };
+ final r = await showWonkyFormDialog(
+ context,
+ WonkyDialogSpec(
+ title: p == null ? l.pubNew : l.pubEdit(p.name),
+ description: p == null ? l.pubNewHint : null,
+ dialogKey: 'dialog.publication',
+ width: 520,
+ fields: [
+ TextFieldSpec(
+ id: 'name',
+ label: l.pubName,
+ initial: p?.name ?? '',
+ maxLength: 60,
+ validator: required,
+ ),
+ TextFieldSpec(
+ id: 'siteUrl',
+ label: l.pubSiteUrl,
+ placeholder: 'https://blog.example.com',
+ initial: p?.siteUrl ?? '',
+ validator: (v) => required(v) ?? https(v),
+ ),
+ TextFieldSpec(
+ id: 'webhookUrl',
+ label: l.pubWebhookUrl,
+ placeholder: 'https://blog.example.com/api/wonky/webhook',
+ initial: p?.webhookUrl ?? '',
+ validator: (v) => required(v) ?? https(v),
+ ),
+ TextFieldSpec(
+ id: 'termsUrl',
+ label: l.pubTermsUrl,
+ initial: p?.termsUrl ?? '',
+ validator: https,
+ ),
+ TextFieldSpec(
+ id: 'template',
+ label: l.pubTemplate,
+ placeholder: l.pubTemplateHint,
+ initial: p?.templateItemUuid == null
+ ? ''
+ : '${s.urls.apps[WonkyAppKind.write]}d/${p!.templateItemUuid!.uuid}',
+ validator: (v) => v.trim().isEmpty || _uuidIn(v) != null
+ ? null
+ : l.pubTemplateInvalid,
+ ),
+ SelectFieldSpec(
+ id: 'summary',
+ label: l.pubFieldSummary,
+ options: modes(),
+ initial: (f?.summary ?? PublicationFieldMode.optional).name,
+ ),
+ SelectFieldSpec(
+ id: 'tags',
+ label: l.pubFieldTags,
+ options: modes(),
+ initial: (f?.tags ?? PublicationFieldMode.optional).name,
+ ),
+ TextFieldSpec(
+ id: 'allowedTags',
+ label: l.pubAllowedTags,
+ placeholder: l.pubAllowedTagsHint,
+ initial: f?.allowedTags.join(', ') ?? '',
+ ),
+ SelectFieldSpec(
+ id: 'coverImage',
+ label: l.pubFieldCover,
+ options: modes(),
+ initial: (f?.coverImage ?? PublicationFieldMode.off).name,
+ ),
+ SelectFieldSpec(
+ id: 'authorBio',
+ label: l.pubFieldBio,
+ options: modes(),
+ initial: (f?.authorBio ?? PublicationFieldMode.off).name,
+ ),
+ SelectFieldSpec(
+ id: 'note',
+ label: l.pubFieldNote,
+ options: modes(),
+ initial: (f?.note ?? PublicationFieldMode.optional).name,
+ ),
+ CheckboxFieldSpec(
+ id: 'enabled',
+ label: l.pubEnabled,
+ initial: p?.enabled ?? true,
+ ),
+ ],
+ buttons: [
+ DialogButtonSpec(
+ id: 'cancel',
+ label: l.actionCancel,
+ validates: false,
+ ),
+ DialogButtonSpec(
+ id: 'save',
+ label: p == null ? l.actionCreate : l.actionSave,
+ primary: true,
+ ),
+ ],
+ ),
+ );
+ if (r == null || r.buttonId != 'save' || !mounted) return;
+ PublicationFieldMode mode(String id) =>
+ PublicationFieldMode.values.asNameMap()[r.text(id)] ??
+ PublicationFieldMode.off;
+ final input = PublicationInput(
+ name: r.text('name').trim(),
+ siteUrl: r.text('siteUrl').trim(),
+ webhookUrl: r.text('webhookUrl').trim(),
+ termsUrl: r.text('termsUrl').trim().isEmpty
+ ? null
+ : r.text('termsUrl').trim(),
+ templateItemUuid: _uuidIn(r.text('template')),
+ fields: PublicationFields(
+ summary: mode('summary'),
+ tags: mode('tags'),
+ allowedTags: [
+ for (final t in r.text('allowedTags').split(','))
+ if (t.trim().isNotEmpty) t.trim(),
+ ],
+ coverImage: mode('coverImage'),
+ authorBio: mode('authorBio'),
+ note: mode('note'),
+ ),
+ enabled: r.flag('enabled'),
+ );
+ if (p == null) {
+ final created = await _guard(() => repo.create(input));
+ if (created == null || !mounted) return;
+ setState(() {
+ _list = [...?_list, created.settings];
+ _open = created.settings.publicId;
+ _secret = (
+ created.settings.publicId,
+ l.pubSigningSecret,
+ created.secret,
+ );
+ _deliveries = null;
+ });
+ await _loadDeliveries(created.settings);
+ } else {
+ final updated = await _guard(
+ () => repo.update(p.publicId, input),
+ );
+ if (updated != null && mounted) _replace(updated);
+ }
+ }
+
+ /// The document uuid in a pasted Wonky Write link (or a bare uuid).
+ static UuidValue? _uuidIn(String text) {
+ final m = RegExp(
+ r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}',
+ ).firstMatch(text);
+ return m == null ? null : UuidValue.fromString(m[0]!.toLowerCase());
+ }
+
+ Future _moderators(PublicationSettings p) async {
+ final l = WonkyAppLocalizations.of(context);
+ final r = await showWonkyFormDialog(
+ context,
+ WonkyDialogSpec(
+ title: l.pubModerators,
+ description: l.pubModeratorsHint,
+ dialogKey: 'dialog.publicationModerators',
+ width: 480,
+ fields: [
+ TextFieldSpec(
+ id: 'emails',
+ label: l.pubModeratorEmails,
+ multiline: true,
+ initial: [for (final m in p.moderators) m.email].join('\n'),
+ ),
+ ],
+ buttons: [
+ DialogButtonSpec(
+ id: 'cancel',
+ label: l.actionCancel,
+ validates: false,
+ ),
+ DialogButtonSpec(id: 'save', label: l.actionSave, primary: true),
+ ],
+ ),
+ );
+ if (r == null || r.buttonId != 'save' || !mounted) return;
+ final emails = [
+ for (final e in r.text('emails').split(RegExp(r'[\s,;]+')))
+ if (e.trim().isNotEmpty) e.trim(),
+ ];
+ final updated = await _guard(
+ () => repo.setModerators(p.publicId, emails),
+ );
+ if (updated != null && mounted) _replace(updated);
+ }
+
+ Future _verify(PublicationSettings p) async {
+ final l = WonkyAppLocalizations.of(context);
+ final r = await _guard(
+ () => repo.verify(p.publicId),
+ );
+ if (r == null || !mounted) return;
+ _replace(r.settings);
+ if (r.ok) {
+ s.toasts.show(l.pubVerified(Uri.parse(p.siteUrl).host));
+ } else {
+ s.toasts.error(l.pubVerifyFailed, description: r.message);
+ }
+ }
+
+ Future _sendTest(PublicationSettings p) async {
+ final l = WonkyAppLocalizations.of(context);
+ final r = await _guard(
+ () => repo.sendTest(p.publicId),
+ );
+ if (r == null || !mounted) return;
+ if (r.ok) {
+ s.toasts.show(l.pubTestOk);
+ } else {
+ s.toasts.error(l.pubTestFailed, description: r.message);
+ }
+ await _loadDeliveries(p);
+ }
+
+ Future _rotateSecret(PublicationSettings p) async {
+ final l = WonkyAppLocalizations.of(context);
+ final ok = await confirmDialog(
+ context,
+ title: l.pubRotateSecret,
+ description: l.pubRotateSecretBody,
+ confirmLabel: l.pubRotate,
+ );
+ if (!ok) return;
+ final r = await _guard(
+ () => repo.rotateSecret(p.publicId),
+ );
+ if (r == null || !mounted) return;
+ _replace(r.settings);
+ setState(() => _secret = (p.publicId, l.pubSigningSecret, r.secret));
+ }
+
+ Future _apiKey(PublicationSettings p) async {
+ final l = WonkyAppLocalizations.of(context);
+ if (p.hasApiKey) {
+ final ok = await confirmDialog(
+ context,
+ title: l.pubRotateApiKey,
+ description: l.pubRotateApiKeyBody,
+ confirmLabel: l.pubRotate,
+ destructive: true,
+ );
+ if (!ok) return;
+ }
+ final r = await _guard(
+ () => repo.createApiKey(p.publicId),
+ );
+ if (r == null || !mounted) return;
+ _replace(r.settings);
+ setState(() => _secret = (p.publicId, l.pubApiKey, r.secret));
+ }
+
+ Future _delete(PublicationSettings p) async {
+ final l = WonkyAppLocalizations.of(context);
+ final ok = await confirmDialog(
+ context,
+ title: l.pubDeleteTitle(p.name),
+ description: l.pubDeleteBody,
+ confirmLabel: l.pubDelete,
+ destructive: true,
+ );
+ if (!ok) return;
+ final done = await _guard(() async {
+ await repo.delete(p.publicId);
+ return true;
+ });
+ if (done == true && mounted) {
+ setState(() {
+ _list = [
+ for (final x in _list ?? const [])
+ if (x.publicId != p.publicId) x,
+ ];
+ _open = null;
+ });
+ }
+ }
+
+ Future _loadDeliveries(PublicationSettings p) async {
+ final list = await _guard>(
+ () => repo.deliveries(p.publicId),
+ );
+ if (list != null && mounted && _open == p.publicId) {
+ setState(() => _deliveries = list);
+ }
+ }
+
+ Future _resend(PublicationSettings p, PublicationDeliveryView d) async {
+ await _guard(
+ () => repo.resend(p.publicId, d.webhookId),
+ );
+ await _loadDeliveries(p);
+ }
+
+ void _toggle(PublicationSettings p) {
+ setState(() {
+ _open = _open == p.publicId ? null : p.publicId;
+ _deliveries = null;
+ });
+ if (_open != null) _loadDeliveries(p);
+ }
+
+ // -------------------------------------------------------------- views
+
+ String _date(DateTime? d) =>
+ d == null ? '—' : intl.DateFormat.yMMMd().add_Hm().format(d.toLocal());
+
+ Widget _mono(BuildContext context, String text, {Key? key}) {
+ final theme = ShadTheme.of(context);
+ return Container(
+ key: key,
+ width: double.infinity,
+ padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
+ decoration: BoxDecoration(
+ color: theme.colorScheme.muted,
+ borderRadius: BorderRadius.circular(6),
+ ),
+ child: Text(
+ text,
+ style: TextStyle(
+ fontFamily: 'packages/shad/GeistMono',
+ fontSize: 12,
+ color: theme.colorScheme.foreground,
+ ),
+ ),
+ );
+ }
+
+ Widget _copyRow(BuildContext context, String label, String value, String id) {
+ final theme = ShadTheme.of(context);
+ final l = WonkyAppLocalizations.of(context);
+ return Padding(
+ padding: const EdgeInsets.only(bottom: 10),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(label, style: theme.textTheme.small),
+ const SizedBox(height: 4),
+ Row(
+ children: [
+ Expanded(child: _mono(context, value)),
+ const SizedBox(width: 6),
+ ShadButton.ghost(
+ key: ValueKey('settings.publications.copy.$id'),
+ size: ShadButtonSize.sm,
+ onPressed: () => _copy(value),
+ child: Text(l.pubCopy),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget _row(BuildContext context, PublicationSettings p) {
+ final theme = ShadTheme.of(context);
+ final l = WonkyAppLocalizations.of(context);
+ final host = Uri.parse(p.siteUrl).host;
+ final open = _open == p.publicId;
+ return Column(
+ key: ValueKey('settings.publications.${p.publicId}'),
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 6),
+ child: Row(
+ children: [
+ Icon(LucideIcons.newspaper, size: 16),
+ const SizedBox(width: 10),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(p.name, style: theme.textTheme.small),
+ Text(
+ [
+ p.verifiedHost != null
+ ? l.pubHostVerified(host)
+ : l.pubHostUnverified(host),
+ if (!p.enabled) l.pubDisabled,
+ l.pubOpenCount(p.openSubmissionCount),
+ ].join(' · '),
+ style: theme.textTheme.muted.copyWith(fontSize: 12),
+ ),
+ ],
+ ),
+ ),
+ ShadButton.ghost(
+ key: ValueKey(
+ 'settings.publications.open.${p.publicId}',
+ ),
+ size: ShadButtonSize.sm,
+ onPressed: () => _toggle(p),
+ child: Text(open ? l.pubHide : l.pubManage),
+ ),
+ ],
+ ),
+ ),
+ if (open) _details(context, p),
+ ],
+ );
+ }
+
+ Widget _details(BuildContext context, PublicationSettings p) {
+ final theme = ShadTheme.of(context);
+ final l = WonkyAppLocalizations.of(context);
+ final secret = _secret;
+ final deliveries = _deliveries;
+ Widget heading(String text) => Padding(
+ padding: const EdgeInsets.only(top: 14, bottom: 6),
+ child: Text(
+ text,
+ style: theme.textTheme.small.copyWith(fontWeight: FontWeight.w600),
+ ),
+ );
+ return Container(
+ padding: const EdgeInsets.fromLTRB(26, 4, 0, 12),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ if (secret != null && secret.$1 == p.publicId)
+ Padding(
+ padding: const EdgeInsets.only(bottom: 12),
+ child: ShadAlert(
+ key: const ValueKey('settings.publications.secret'),
+ icon: const Icon(LucideIcons.keyRound),
+ title: Text(l.pubSecretOnce(secret.$2)),
+ description: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const SizedBox(height: 6),
+ _mono(context, secret.$3),
+ const SizedBox(height: 6),
+ Row(
+ children: [
+ ShadButton.outline(
+ size: ShadButtonSize.sm,
+ onPressed: () => _copy(secret.$3),
+ child: Text(l.pubCopy),
+ ),
+ const SizedBox(width: 6),
+ ShadButton.ghost(
+ size: ShadButtonSize.sm,
+ onPressed: () => setState(() => _secret = null),
+ child: Text(l.pubSecretSaved),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ _copyRow(context, l.pubContributorLink, p.contributorLink, 'link'),
+ _copyRow(context, l.pubId, p.publicId, 'id'),
+ Wrap(
+ spacing: 6,
+ runSpacing: 6,
+ children: [
+ ShadButton.outline(
+ key: const ValueKey('settings.publications.edit'),
+ size: ShadButtonSize.sm,
+ onPressed: () => _edit(p),
+ child: Text(l.pubEditButton),
+ ),
+ ShadButton.outline(
+ key: const ValueKey('settings.publications.moderators'),
+ size: ShadButtonSize.sm,
+ onPressed: () => _moderators(p),
+ child: Text(l.pubModeratorsButton(p.moderators.length)),
+ ),
+ ShadButton.ghost(
+ key: const ValueKey('settings.publications.delete'),
+ size: ShadButtonSize.sm,
+ onPressed: () => _delete(p),
+ child: Text(l.pubDelete),
+ ),
+ ],
+ ),
+ heading(l.pubVerification),
+ Text(
+ p.verifiedHost != null
+ ? l.pubVerifiedAt(p.verifiedHost!, _date(p.verifiedAt))
+ : l.pubVerifyHint(Uri.parse(p.siteUrl).host),
+ style: theme.textTheme.muted.copyWith(fontSize: 12),
+ ),
+ if (p.verifiedHost == null) ...[
+ const SizedBox(height: 6),
+ _mono(context, '{ "publications": ["${p.publicId}"] }'),
+ ],
+ const SizedBox(height: 6),
+ ShadButton.outline(
+ key: const ValueKey('settings.publications.verify'),
+ size: ShadButtonSize.sm,
+ onPressed: () => _verify(p),
+ child: Text(l.pubVerify),
+ ),
+ heading(l.pubCredentials),
+ Text(
+ p.previousSecretUntil != null
+ ? l.pubRotationActive(_date(p.previousSecretUntil))
+ : l.pubSecretHint,
+ style: theme.textTheme.muted.copyWith(fontSize: 12),
+ ),
+ const SizedBox(height: 6),
+ Wrap(
+ spacing: 6,
+ runSpacing: 6,
+ children: [
+ ShadButton.outline(
+ key: const ValueKey('settings.publications.rotate'),
+ size: ShadButtonSize.sm,
+ onPressed: () => _rotateSecret(p),
+ child: Text(l.pubRotateSecret),
+ ),
+ ShadButton.outline(
+ key: const ValueKey('settings.publications.apiKey'),
+ size: ShadButtonSize.sm,
+ onPressed: () => _apiKey(p),
+ child: Text(
+ p.hasApiKey
+ ? l.pubRotateApiKeyLast4(p.apiKeyLast4 ?? '')
+ : l.pubCreateApiKey,
+ ),
+ ),
+ ],
+ ),
+ heading(l.pubDeliveries),
+ Row(
+ children: [
+ ShadButton.outline(
+ key: const ValueKey('settings.publications.test'),
+ size: ShadButtonSize.sm,
+ onPressed: () => _sendTest(p),
+ child: Text(l.pubSendTest),
+ ),
+ ],
+ ),
+ const SizedBox(height: 6),
+ if (deliveries == null)
+ const ShadSpinner()
+ else if (deliveries.isEmpty)
+ Text(l.pubNoDeliveries, style: theme.textTheme.muted)
+ else
+ for (final d in deliveries) _delivery(context, p, d),
+ ],
+ ),
+ );
+ }
+
+ Widget _delivery(
+ BuildContext context,
+ PublicationSettings p,
+ PublicationDeliveryView d,
+ ) {
+ final theme = ShadTheme.of(context);
+ final l = WonkyAppLocalizations.of(context);
+ final state = d.deliveredAt != null
+ ? l.pubDelivered(d.lastStatus ?? 200)
+ : d.failedAt != null
+ ? l.pubDeliveryFailed(d.lastError ?? '')
+ : d.attempts == 0
+ ? l.pubDeliveryQueued
+ : l.pubDeliveryRetrying(d.lastError ?? '', _date(d.nextAttemptAt));
+ return Padding(
+ key: ValueKey('settings.publications.delivery.${d.webhookId}'),
+ padding: const EdgeInsets.symmetric(vertical: 4),
+ child: Row(
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ '${d.type} · ${_date(d.createdAt)}',
+ style: theme.textTheme.small.copyWith(fontSize: 12),
+ ),
+ Text(
+ state,
+ style: theme.textTheme.muted.copyWith(fontSize: 12),
+ ),
+ ],
+ ),
+ ),
+ ShadButton.ghost(
+ size: ShadButtonSize.sm,
+ onPressed: () => _resend(p, d),
+ child: Text(l.pubResend),
+ ),
+ ],
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final l = WonkyAppLocalizations.of(context);
+ final theme = ShadTheme.of(context);
+ final list = _list;
+ return SettingsGroup(
+ title: l.settingsPublications,
+ description: l.pubSectionHint,
+ children: [
+ Wrap(
+ spacing: 6,
+ runSpacing: 6,
+ children: [
+ ShadButton(
+ key: const ValueKey('settings.publications.new'),
+ size: ShadButtonSize.sm,
+ leading: const Icon(LucideIcons.plus, size: 16),
+ onPressed: _edit,
+ child: Text(l.pubNew),
+ ),
+ ShadButton.ghost(
+ key: const ValueKey('settings.publications.guide'),
+ size: ShadButtonSize.sm,
+ leading: const Icon(LucideIcons.bookOpen, size: 16),
+ onPressed: () => s.crossApp.openExternal(publicationGuideUrl),
+ child: Text(l.pubGuide),
+ ),
+ ],
+ ),
+ const SizedBox(height: 10),
+ if (_loadError != null)
+ Text(errorMessage(_loadError!), style: theme.textTheme.muted)
+ else if (list == null)
+ const ShadSpinner()
+ else if (list.isEmpty)
+ Text(
+ l.pubEmpty,
+ key: const ValueKey('settings.publications.empty'),
+ style: theme.textTheme.muted,
+ )
+ else
+ for (final p in list) _row(context, p),
+ ],
+ );
+ }
+}
diff --git a/packages/wonky_app/lib/src/publications/start_story.dart b/packages/wonky_app/lib/src/publications/start_story.dart
new file mode 100644
index 00000000..de097b51
--- /dev/null
+++ b/packages/wonky_app/lib/src/publications/start_story.dart
@@ -0,0 +1,95 @@
+import 'package:flutter/widgets.dart';
+import 'package:wonky_client/wonky_client.dart';
+
+import '../l10n/wonky_app_localizations.dart';
+import '../shell/form_dialog.dart';
+import '../shell/shell_services.dart';
+
+/// The `/new?publication=pub_…` route of Write (specs/18 §3): asks for
+/// consent (once per publication, again when what was agreed changed),
+/// offers the contributor's open stories, and creates the document of a
+/// new one. Returns the document to open, or null when cancelled.
+Future startPublicationStory(
+ BuildContext context,
+ ShellServices services,
+ String publicId, {
+ String? ref,
+}) async {
+ final repo = services.repositories.publications;
+ final l = WonkyAppLocalizations.of(context);
+ final card = await repo.resolve(publicId);
+ if (!context.mounted) return null;
+ if (card.blocked || !card.enabled) {
+ await showWonkyFormDialog(
+ context,
+ WonkyDialogSpec(
+ title: card.name,
+ description: card.blocked
+ ? l.pubStartBlocked(card.name)
+ : l.pubStartClosed(card.name),
+ dialogKey: 'dialog.publicationStart',
+ alert: true,
+ buttons: [DialogButtonSpec(id: 'cancel', label: l.actionOk)],
+ ),
+ );
+ return null;
+ }
+ if (!card.consentNeeded && card.openSubmissions.isEmpty) {
+ final started = await repo.startSubmission(
+ publicId,
+ ref: ref,
+ agreed: false,
+ );
+ return started.itemUuid;
+ }
+ final host = card.verified
+ ? l.pubStartHostVerified(card.shownHost)
+ : l.pubStartHostUnverified(card.siteHost, card.webhookHost);
+ final r = await showWonkyFormDialog(
+ context,
+ WonkyDialogSpec(
+ title: l.pubStartTitle(card.name),
+ description: [
+ host,
+ if (card.consentNeeded) l.pubStartConsent(card.name),
+ ].join('\n\n'),
+ dialogKey: 'dialog.publicationStart',
+ width: 480,
+ fields: [
+ if (card.openSubmissions.isNotEmpty)
+ SelectFieldSpec(
+ id: 'story',
+ label: l.pubStartWhich,
+ options: {
+ for (final o in card.openSubmissions)
+ o.itemUuid.uuid: l.pubStartContinue(o.name),
+ 'new': l.pubStartNew,
+ },
+ initial: card.openSubmissions.first.itemUuid.uuid,
+ ),
+ if (card.consentNeeded && card.termsUrl != null)
+ CheckboxFieldSpec(
+ id: 'terms',
+ label: l.pubStartTerms(card.name, card.termsUrl!),
+ ),
+ ],
+ buttons: [
+ DialogButtonSpec(id: 'cancel', label: l.actionCancel, validates: false),
+ DialogButtonSpec(id: 'start', label: l.pubStartButton, primary: true),
+ ],
+ ),
+ );
+ if (r == null || r.buttonId != 'start') return null;
+ final story = r.values['story'] as String?;
+ if (story != null && story != 'new') return UuidValue.fromString(story);
+ if (card.consentNeeded && card.termsUrl != null && !r.flag('terms')) {
+ services.toasts.error(l.pubStartTermsRequired(card.name));
+ return null;
+ }
+ final started = await repo.startSubmission(
+ publicId,
+ ref: ref,
+ agreed: card.consentNeeded,
+ );
+ return started.itemUuid;
+}
diff --git a/packages/wonky_app/lib/src/publications/submission_undo.dart b/packages/wonky_app/lib/src/publications/submission_undo.dart
new file mode 100644
index 00000000..f1f167e5
--- /dev/null
+++ b/packages/wonky_app/lib/src/publications/submission_undo.dart
@@ -0,0 +1,47 @@
+import 'package:wonky_client/wonky_client.dart';
+import 'package:wonky_core/wonky_core.dart' hide ActorKind;
+
+import '../shell/shell_services.dart';
+
+/// Undo of disconnecting a document from its publication (specs/18 §4.4):
+/// undo reconnects, redo disconnects again. Submitting and withdrawing
+/// reach the blog and are not undoable.
+class SubmissionUndoEntry extends UndoEntry {
+ const SubmissionUndoEntry({required this.itemUuid});
+
+ final String itemUuid;
+
+ static const domainName = 'publications';
+
+ @override
+ String get domain => domainName;
+
+ @override
+ UndoEntry? rebase(RemoteChange change) => this;
+}
+
+/// Applies [SubmissionUndoEntry]s through the publication repository.
+class SubmissionUndoHandler extends UndoHandler {
+ SubmissionUndoHandler(this.services);
+
+ final ShellServices services;
+
+ @override
+ String get domain => SubmissionUndoEntry.domainName;
+
+ @override
+ Future undo(SubmissionUndoEntry e) async {
+ await services.repositories.publications.reconnect(
+ UuidValue.fromString(e.itemUuid),
+ );
+ return e;
+ }
+
+ @override
+ Future redo(SubmissionUndoEntry e) async {
+ await services.repositories.publications.disconnect(
+ UuidValue.fromString(e.itemUuid),
+ );
+ return e;
+ }
+}
diff --git a/packages/wonky_app/lib/src/settings/sections/other_sections.dart b/packages/wonky_app/lib/src/settings/sections/other_sections.dart
index 954c446a..58515d32 100644
--- a/packages/wonky_app/lib/src/settings/sections/other_sections.dart
+++ b/packages/wonky_app/lib/src/settings/sections/other_sections.dart
@@ -106,7 +106,8 @@ class NotificationsSection extends StatelessWidget {
? p.notificationSettings['shares']
: null))
as String? ??
- 'inApp';
+ // Stories for publications email by default (specs/18 §5.3).
+ (k == 'submissions' ? 'email' : 'inApp');
Widget choice(String k) => ShadSelect(
key: ValueKey('settings.notifications.$k'),
initialValue: v(k),
@@ -141,6 +142,11 @@ class NotificationsSection extends StatelessWidget {
description: l.notifyBoardHint,
control: choice('board'),
),
+ SettingsRow(
+ label: l.notifySubmissions,
+ description: l.notifySubmissionsHint,
+ control: choice('submissions'),
+ ),
SettingsRow(
label: l.notifyDigest,
description: l.notifyDigestHint,
diff --git a/packages/wonky_app/lib/src/settings/settings_catalog.dart b/packages/wonky_app/lib/src/settings/settings_catalog.dart
index 8f66b4d9..e685b86a 100644
--- a/packages/wonky_app/lib/src/settings/settings_catalog.dart
+++ b/packages/wonky_app/lib/src/settings/settings_catalog.dart
@@ -27,6 +27,7 @@ const workSettingsSectionIds = [
'ai',
'ai-providers',
'developer',
+ 'publications',
'about',
];
diff --git a/packages/wonky_app/lib/src/settings/settings_screen.dart b/packages/wonky_app/lib/src/settings/settings_screen.dart
index f17bbcff..c7df775f 100644
--- a/packages/wonky_app/lib/src/settings/settings_screen.dart
+++ b/packages/wonky_app/lib/src/settings/settings_screen.dart
@@ -14,6 +14,7 @@ import 'sections/account_section.dart';
import 'sections/appearance_section.dart';
import 'sections/keyboard_section.dart';
import 'sections/other_sections.dart';
+import '../publications/publications_section.dart';
import 'sections/security_section.dart';
import 'sections/storage_section.dart';
import 'settings_catalog.dart';
@@ -132,6 +133,14 @@ class SettingsScreen extends StatelessWidget {
LucideIcons.squareTerminal,
(_) => DeveloperSection(services: s),
),
+ // (M14) Blogs that take stories from Wonky Write (specs/18 §8.1).
+ if (s.repositories.hasPublications)
+ w(
+ 'publications',
+ l.settingsPublications,
+ LucideIcons.newspaper,
+ (_) => PublicationsSection(services: s),
+ ),
w(
'about',
l.settingsAbout,
diff --git a/packages/wonky_app/lib/src/sharing/share_dialog.dart b/packages/wonky_app/lib/src/sharing/share_dialog.dart
index 504c9781..e9742733 100644
--- a/packages/wonky_app/lib/src/sharing/share_dialog.dart
+++ b/packages/wonky_app/lib/src/sharing/share_dialog.dart
@@ -347,6 +347,7 @@ class _ShareDialogState extends State {
p.role,
invitedEmail: p.user == null ? p.invitedEmail : null,
inheritedFrom: p.inheritedFromName,
+ moderatorOf: p.viaPublication,
isMe: p.user?.id == me,
editable: canShare && p.inheritedFromUuid == null,
),
@@ -583,6 +584,7 @@ class _ShareDialogState extends State {
bool editable = false,
String? invitedEmail,
String? inheritedFrom,
+ String? moderatorOf,
}) {
final l = WonkyAppLocalizations.of(context);
final theme = ShadTheme.of(context);
@@ -593,6 +595,7 @@ class _ShareDialogState extends State {
if (user != null) email,
if (invitedEmail != null) l.sharePending,
if (inheritedFrom != null) l.shareInherited(inheritedFrom),
+ if (moderatorOf != null) l.shareModerator(moderatorOf),
].join(' · ');
Widget trailing;
if (isOwnerRow || !editable || isMe) {
diff --git a/packages/wonky_app/lib/src/shell/dialogs.dart b/packages/wonky_app/lib/src/shell/dialogs.dart
index aec3bda6..996b7b47 100644
--- a/packages/wonky_app/lib/src/shell/dialogs.dart
+++ b/packages/wonky_app/lib/src/shell/dialogs.dart
@@ -213,9 +213,54 @@ String activityText(WonkyAppLocalizations l, ActivityEventView e) {
ActivityType.restored => l.activityRestored(who),
ActivityType.exported => l.activityExported(who),
ActivityType.uploaded => l.activityUploaded(who),
+ ActivityType.submission => _submissionActivity(l, e, who),
};
}
+/// (M14) Submitted to, withdrawn from or disconnected from a publication,
+/// or the publication's status change (specs/18 §6).
+String _submissionActivity(
+ WonkyAppLocalizations l,
+ ActivityEventView e,
+ String who,
+) {
+ Map m;
+ try {
+ final v = jsonDecode(e.summary);
+ m = v is Map ? v.cast() : const {};
+ } on FormatException {
+ m = const {};
+ }
+ final pub = '${m['publication'] ?? ''}';
+ return switch (m['action']) {
+ 'withdrawn' => l.activitySubmissionWithdrawn(who, pub),
+ 'disconnected' => l.activitySubmissionDisconnected(who, pub),
+ 'reconnected' => l.activitySubmissionReconnected(who, pub),
+ 'status' => l.activitySubmissionStatus(
+ pub,
+ submissionStatusLabel(
+ l,
+ SubmissionStatus.values.asNameMap()['${m['status']}'] ??
+ SubmissionStatus.inReview,
+ ),
+ ),
+ _ => l.activitySubmitted(who, pub),
+ };
+}
+
+/// The label of a submission status (specs/18 §2.1).
+String submissionStatusLabel(
+ WonkyAppLocalizations l,
+ SubmissionStatus status,
+) => switch (status) {
+ SubmissionStatus.draft => l.submissionStatusDraft,
+ SubmissionStatus.inReview => l.submissionStatusInReview,
+ SubmissionStatus.changesRequested => l.submissionStatusChangesRequested,
+ SubmissionStatus.declined => l.submissionStatusDeclined,
+ SubmissionStatus.published => l.submissionStatusPublished,
+ SubmissionStatus.withdrawn => l.submissionStatusWithdrawn,
+};
+
/// The detail line of an edit event (Slides 09 §5): "3 slides · 1 slide
/// deleted · theme changed · with Claude", from the counters and sets of
/// its summary; null when there is nothing to add (documents,
diff --git a/packages/wonky_app/lib/src/shell/shell_services.dart b/packages/wonky_app/lib/src/shell/shell_services.dart
index 47305586..1a466f72 100644
--- a/packages/wonky_app/lib/src/shell/shell_services.dart
+++ b/packages/wonky_app/lib/src/shell/shell_services.dart
@@ -35,6 +35,8 @@ import '../mcp_bridge/mcp_control.dart';
import '../storage/storage_controller.dart';
import '../storage/quota_error_presenter.dart';
import '../board/board_repository.dart';
+import '../publications/publication_repository.dart';
+import '../publications/submission_undo.dart';
import '../storage/storage_checks.dart';
import '../menus/shell_menus.dart';
import '../undo/drive_undo.dart';
@@ -68,8 +70,10 @@ class ShellRepositories {
required this.meta,
ActivityRepository? activity,
BoardRepository? board,
+ PublicationRepository? publications,
}) : _activity = activity, // ignore: prefer_initializing_formals
- _board = board; // ignore: prefer_initializing_formals
+ _board = board, // ignore: prefer_initializing_formals
+ _publications = publications; // ignore: prefer_initializing_formals
final DriveRepository drive;
final UserRepository users;
@@ -82,6 +86,16 @@ class ShellRepositories {
final MetaRepository meta;
final ActivityRepository? _activity;
final BoardRepository? _board;
+ final PublicationRepository? _publications;
+
+ /// Publications and submissions (specs/18); throws when the shell was
+ /// built without one.
+ PublicationRepository get publications =>
+ _publications ??
+ (throw StateError('No PublicationRepository installed.'));
+
+ /// Whether [publications] is available.
+ bool get hasPublications => _publications != null;
/// Bugs & improvements (specs/16); throws when the shell was built
/// without one.
@@ -130,6 +144,9 @@ class ShellServices implements ActiveDocumentHost {
);
undo.registerHandler(DriveUndoHandler(repositories.drive));
undo.registerHandler(CommentsUndoHandler(this));
+ if (repositories.hasPublications) {
+ undo.registerHandler(SubmissionUndoHandler(this));
+ }
undo.registerHandler(VersionsUndoHandler(this));
quotaErrors.attach();
}
diff --git a/packages/wonky_app/lib/src/shell/wonky_shell.dart b/packages/wonky_app/lib/src/shell/wonky_shell.dart
index d41cf7ac..3a37ab36 100644
--- a/packages/wonky_app/lib/src/shell/wonky_shell.dart
+++ b/packages/wonky_app/lib/src/shell/wonky_shell.dart
@@ -32,6 +32,7 @@ import '../mcp_bridge/mcp_bridge_scope.dart';
import '../routing/router.dart';
import '../storage/live_storage_controller.dart';
import '../board/board_repository.dart';
+import '../publications/publication_repository.dart';
import 'overlays/shell_overlays.dart';
import 'shell_services.dart';
import '../undo/undo_service.dart';
@@ -141,6 +142,7 @@ class WonkyShell extends StatefulWidget {
meta: ServerMetaRepository(api),
activity: ServerActivityRepository(api),
board: ServerBoardRepository(api),
+ publications: ServerPublicationRepository(api),
),
urls: wc.urls,
connectivity: connectivity,
diff --git a/packages/wonky_app/lib/src/testing/command_test_harness.dart b/packages/wonky_app/lib/src/testing/command_test_harness.dart
index b5195b36..81716ee5 100644
--- a/packages/wonky_app/lib/src/testing/command_test_harness.dart
+++ b/packages/wonky_app/lib/src/testing/command_test_harness.dart
@@ -25,6 +25,8 @@ import '../spellcheck/shell_spelling.dart';
import '../storage/storage_controller.dart';
import '../undo/undo_service.dart';
import '../board/board_repository.dart';
+import '../publications/publication_repository.dart';
+import 'fake_publication_repository.dart';
import 'fake_cross_app.dart';
import 'fake_repositories.dart';
@@ -106,6 +108,7 @@ class CommandTestHarness {
MetaRepository? meta,
ActivityRepository? activity,
BoardRepository? board,
+ PublicationRepository? publications,
LocalStore? store,
UuidV7Generator? ids,
StorageController? storage,
@@ -140,6 +143,7 @@ class CommandTestHarness {
meta: meta ?? UnfakedMetaRepository(),
activity: activity,
board: board,
+ publications: publications ?? FakePublicationRepository(),
),
urls: urls,
crossApp: crossApp ?? FakeCrossApp(urls),
diff --git a/packages/wonky_app/lib/src/testing/fake_publication_repository.dart b/packages/wonky_app/lib/src/testing/fake_publication_repository.dart
new file mode 100644
index 00000000..e180aa12
--- /dev/null
+++ b/packages/wonky_app/lib/src/testing/fake_publication_repository.dart
@@ -0,0 +1,221 @@
+import 'package:wonky_client/wonky_client.dart';
+
+import '../publications/publication_repository.dart';
+
+/// An in-memory [PublicationRepository] (suite 15 §5): one contributor
+/// card, document submissions by item, and the owner's publications. Calls
+/// are recorded in [calls].
+class FakePublicationRepository implements PublicationRepository {
+ FakePublicationRepository({this.card});
+
+ /// What [resolve] answers.
+ PublicationCard? card;
+
+ /// Submissions by document.
+ final submissions = {};
+
+ /// The owner's publications.
+ final publications = [];
+
+ /// What [startSubmission] creates.
+ UuidValue startedItem = UuidValue.fromString(
+ '01900000-0000-7000-8000-0000000000aa',
+ );
+
+ /// The calls made, e.g. `submit`, `startSubmission:agreed`.
+ final calls = [];
+
+ /// The values of the last [submit].
+ SubmissionValues? lastValues;
+
+ static PublicationFields fields() => PublicationFields(
+ summary: PublicationFieldMode.optional,
+ tags: PublicationFieldMode.optional,
+ allowedTags: [],
+ coverImage: PublicationFieldMode.off,
+ authorBio: PublicationFieldMode.off,
+ note: PublicationFieldMode.optional,
+ );
+
+ /// A submission view with defaults.
+ static SubmissionView view(
+ UuidValue itemUuid, {
+ SubmissionStatus status = SubmissionStatus.draft,
+ String name = 'Serverpod blog',
+ bool accepting = true,
+ int? lastSubmittedRevision,
+ String? lastMessage,
+ String? publicUrl,
+ UuidValue? feedbackThreadUuid,
+ SubmissionDeliveryState? delivery,
+ }) => SubmissionView(
+ id: 'sub_${itemUuid.uuid}',
+ itemUuid: itemUuid,
+ status: status,
+ delivery: delivery,
+ publicationId: 'pub_AAAAAAAAAAAAAAAAAAAAAA',
+ publicationName: name,
+ shownHost: 'blog.example.com',
+ verified: true,
+ accepting: accepting,
+ fields: fields(),
+ consentNeeded: false,
+ values: SubmissionValues(title: 'Untitled story', tags: []),
+ publicUrl: publicUrl,
+ feedbackThreadUuid: feedbackThreadUuid,
+ lastMessage: lastMessage,
+ lastSubmittedRevision: lastSubmittedRevision,
+ revisions: [],
+ updatedAt: DateTime.utc(2026, 10, 20),
+ );
+
+ @override
+ Future resolve(String publicId) async {
+ calls.add('resolve');
+ return card!;
+ }
+
+ @override
+ Future startSubmission(
+ String publicId, {
+ String? ref,
+ required bool agreed,
+ }) async {
+ calls.add(agreed ? 'startSubmission:agreed' : 'startSubmission');
+ submissions[startedItem] = view(startedItem);
+ return SubmissionStarted(itemUuid: startedItem);
+ }
+
+ @override
+ Future submission(UuidValue itemUuid) async =>
+ submissions[itemUuid];
+
+ @override
+ Future submit(
+ UuidValue itemUuid,
+ SubmissionValues values, {
+ required bool agreed,
+ }) async {
+ calls.add('submit');
+ lastValues = values;
+ final v = submissions[itemUuid]!.copyWith(
+ status: SubmissionStatus.inReview,
+ values: values,
+ lastSubmittedRevision: 1 << 30,
+ );
+ return submissions[itemUuid] = v;
+ }
+
+ @override
+ Future withdraw(UuidValue itemUuid) async {
+ calls.add('withdraw');
+ return submissions[itemUuid] = submissions[itemUuid]!.copyWith(
+ status: SubmissionStatus.withdrawn,
+ );
+ }
+
+ final _disconnected = {};
+
+ @override
+ Future disconnect(UuidValue itemUuid) async {
+ calls.add('disconnect');
+ _disconnected[itemUuid] = submissions.remove(itemUuid)!;
+ }
+
+ @override
+ Future reconnect(UuidValue itemUuid) async {
+ calls.add('reconnect');
+ final v = _disconnected.remove(itemUuid);
+ if (v != null) submissions[itemUuid] = v;
+ return v;
+ }
+
+ PublicationSettings _settings(PublicationInput input, String id) =>
+ PublicationSettings(
+ publicId: id,
+ name: input.name,
+ siteUrl: input.siteUrl,
+ webhookUrl: input.webhookUrl,
+ termsUrl: input.termsUrl,
+ fields: input.fields,
+ moderators: [],
+ hasApiKey: false,
+ enabled: input.enabled,
+ contributorLink: 'https://wonky.work/write/new?publication=$id',
+ openSubmissionCount: 0,
+ createdAt: DateTime.utc(2026, 10, 20),
+ );
+
+ @override
+ Future> list() async => [...publications];
+
+ @override
+ Future create(PublicationInput input) async {
+ calls.add('create');
+ final p = _settings(input, 'pub_${'B' * 22}');
+ publications.add(p);
+ return PublicationSecret(settings: p, secret: 'whsec_c2VjcmV0');
+ }
+
+ @override
+ Future update(
+ String publicId,
+ PublicationInput input,
+ ) async {
+ calls.add('update');
+ final p = _settings(input, publicId);
+ publications[publications.indexWhere((x) => x.publicId == publicId)] = p;
+ return p;
+ }
+
+ @override
+ Future delete(String publicId) async {
+ calls.add('delete');
+ publications.removeWhere((p) => p.publicId == publicId);
+ }
+
+ PublicationSettings _one(String id) =>
+ publications.firstWhere((p) => p.publicId == id);
+
+ @override
+ Future verify(String publicId) async =>
+ PublicationCheckResult(
+ ok: false,
+ message: 'HTTP 404',
+ settings: _one(publicId),
+ );
+
+ @override
+ Future rotateSecret(String publicId) async =>
+ PublicationSecret(settings: _one(publicId), secret: 'whsec_bmV3');
+
+ @override
+ Future createApiKey(String publicId) async {
+ calls.add('createApiKey');
+ return PublicationSecret(
+ settings: _one(publicId).copyWith(hasApiKey: true, apiKeyLast4: 'abcd'),
+ secret: 'wpk_${'k' * 40}',
+ );
+ }
+
+ @override
+ Future setModerators(
+ String publicId,
+ List emails,
+ ) async => _one(publicId).copyWith(
+ moderators: [for (final e in emails) PublicationModeratorView(email: e)],
+ );
+
+ @override
+ Future> deliveries(String publicId) async => [];
+
+ @override
+ Future resend(
+ String publicId,
+ String webhookId,
+ ) async => throw UnimplementedError();
+
+ @override
+ Future sendTest(String publicId) async =>
+ PublicationCheckResult(ok: true, settings: _one(publicId));
+}
diff --git a/packages/wonky_app/lib/testing.dart b/packages/wonky_app/lib/testing.dart
index f7d36b2a..d4bf3e98 100644
--- a/packages/wonky_app/lib/testing.dart
+++ b/packages/wonky_app/lib/testing.dart
@@ -17,6 +17,7 @@ export 'src/testing/command_sweep.dart';
export 'src/testing/command_test_harness.dart';
export 'src/testing/fake_board_repository.dart';
export 'src/testing/fake_cross_app.dart';
+export 'src/testing/fake_publication_repository.dart';
export 'src/testing/fake_storage.dart';
export 'src/testing/fake_repositories.dart'
hide
diff --git a/packages/wonky_app/lib/wonky_app.dart b/packages/wonky_app/lib/wonky_app.dart
index 28607a54..79f964ea 100644
--- a/packages/wonky_app/lib/wonky_app.dart
+++ b/packages/wonky_app/lib/wonky_app.dart
@@ -53,6 +53,10 @@ export 'src/core/toasts.dart';
export 'src/board/board_commands.dart';
export 'src/board/board_hub.dart';
export 'src/board/board_repository.dart';
+export 'src/publications/publication_repository.dart';
+export 'src/publications/publications_section.dart';
+export 'src/publications/start_story.dart';
+export 'src/publications/submission_undo.dart';
export 'src/board/board_undo.dart';
export 'src/board/pages/board_page.dart' show BoardPage;
export 'src/board/pages/board_routes.dart';
diff --git a/packages/wonky_app/test/publications/publications_section_test.dart b/packages/wonky_app/test/publications/publications_section_test.dart
new file mode 100644
index 00000000..f1ef6a2f
--- /dev/null
+++ b/packages/wonky_app/test/publications/publications_section_test.dart
@@ -0,0 +1,124 @@
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:wonky_app/testing.dart';
+import 'package:wonky_app/wonky_app.dart';
+import 'package:wonky_client/wonky_client.dart';
+
+import '../support/screen_pump.dart';
+
+Finder _key(String k) => find.byKey(ValueKey(k));
+
+void main() {
+ late CommandTestHarness h;
+ late FakePublicationRepository repo;
+
+ setUp(() {
+ repo = FakePublicationRepository();
+ h = CommandTestHarness(publications: repo);
+ });
+ tearDown(() => h.dispose());
+
+ Future pump(WidgetTester tester, Brightness b) => pumpScreen(
+ tester,
+ h,
+ SingleChildScrollView(child: PublicationsSection(services: h.services)),
+ brightness: b,
+ );
+
+ for (final b in Brightness.values) {
+ group('Given no publications (${b.name})', () {
+ testWidgets('then the empty state shows', (tester) async {
+ await pump(tester, b);
+
+ expect(_key('settings.publications.empty'), findsOneWidget);
+ });
+
+ testWidgets('when creating one then the signing secret is shown once '
+ 'with the contributor link', (tester) async {
+ await pump(tester, b);
+
+ await tester.tap(_key('settings.publications.new'));
+ await tester.pumpAndSettle();
+ await tester.enterText(_key('dialog.publication.name'), 'My blog');
+ await tester.enterText(
+ _key('dialog.publication.siteUrl'),
+ 'https://blog.example.com',
+ );
+ await tester.enterText(
+ _key('dialog.publication.webhookUrl'),
+ 'https://blog.example.com/hook',
+ );
+ await tester.tap(_key('dialog.publication.save'));
+ await tester.pumpAndSettle();
+
+ expect(repo.calls, contains('create'));
+ expect(_key('settings.publications.secret'), findsOneWidget);
+ expect(find.text('whsec_c2VjcmV0'), findsOneWidget);
+ expect(
+ find.text('https://wonky.work/write/new?publication=pub_${'B' * 22}'),
+ findsOneWidget,
+ );
+ await tester.tap(find.text("I've stored it"));
+ await tester.pumpAndSettle();
+ expect(find.text('whsec_c2VjcmV0'), findsNothing);
+ });
+
+ testWidgets('when the webhook is not https then it is refused', (
+ tester,
+ ) async {
+ await pump(tester, b);
+
+ await tester.tap(_key('settings.publications.new'));
+ await tester.pumpAndSettle();
+ await tester.enterText(_key('dialog.publication.name'), 'My blog');
+ await tester.enterText(
+ _key('dialog.publication.siteUrl'),
+ 'https://blog.example.com',
+ );
+ await tester.enterText(
+ _key('dialog.publication.webhookUrl'),
+ 'http://blog.example.com/hook',
+ );
+ await tester.tap(_key('dialog.publication.save'));
+ await tester.pumpAndSettle();
+
+ expect(repo.calls, isNot(contains('create')));
+ expect(find.text('Use an https:// address.'), findsOneWidget);
+ });
+ });
+ }
+
+ group('Given a publication', () {
+ setUp(() {
+ repo.publications.add(
+ PublicationSettings(
+ publicId: 'pub_${'C' * 22}',
+ name: 'Serverpod blog',
+ siteUrl: 'https://blog.example.com',
+ webhookUrl: 'https://blog.example.com/hook',
+ fields: FakePublicationRepository.fields(),
+ moderators: [],
+ hasApiKey: false,
+ enabled: true,
+ contributorLink: 'https://wonky.work/write/new?publication=x',
+ openSubmissionCount: 2,
+ createdAt: DateTime.utc(2026, 10, 20),
+ ),
+ );
+ });
+
+ testWidgets('when managing it and creating an API key then the key is '
+ 'shown once', (tester) async {
+ await pump(tester, Brightness.light);
+
+ expect(find.textContaining('2 stories in review'), findsOneWidget);
+ await tester.tap(_key('settings.publications.open.pub_${'C' * 22}'));
+ await tester.pumpAndSettle();
+ await tester.tap(_key('settings.publications.apiKey'));
+ await tester.pumpAndSettle();
+
+ expect(repo.calls, contains('createApiKey'));
+ expect(find.text('wpk_${'k' * 40}'), findsOneWidget);
+ });
+ });
+}
diff --git a/packages/wonky_app/test/publications/start_story_test.dart b/packages/wonky_app/test/publications/start_story_test.dart
new file mode 100644
index 00000000..e043acc3
--- /dev/null
+++ b/packages/wonky_app/test/publications/start_story_test.dart
@@ -0,0 +1,182 @@
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:wonky_app/testing.dart';
+import 'package:wonky_app/wonky_app.dart';
+import 'package:wonky_client/wonky_client.dart';
+
+import '../support/screen_pump.dart';
+
+Finder _key(String k) => find.byKey(ValueKey(k));
+
+/// Runs [startPublicationStory] from a button so it gets a context below
+/// the shell.
+class _Starter extends StatelessWidget {
+ const _Starter({required this.onDone});
+
+ final void Function(UuidValue?) onDone;
+
+ @override
+ Widget build(BuildContext context) => GestureDetector(
+ key: const ValueKey('start'),
+ behavior: HitTestBehavior.opaque,
+ onTap: () async {
+ final s = ShellScope.of(context);
+ onDone(
+ await startPublicationStory(context, s, 'pub_${'A' * 22}', ref: 'x'),
+ );
+ },
+ child: const SizedBox(width: 100, height: 40),
+ );
+}
+
+PublicationCard _card({
+ bool consentNeeded = true,
+ String? termsUrl,
+ List open = const [],
+ bool blocked = false,
+ bool verified = true,
+}) => PublicationCard(
+ publicId: 'pub_${'A' * 22}',
+ name: 'Serverpod blog',
+ shownHost: verified ? 'blog.example.com' : 'hooks.example.net',
+ verified: verified,
+ siteHost: 'blog.example.com',
+ webhookHost: 'hooks.example.net',
+ termsUrl: termsUrl,
+ fields: FakePublicationRepository.fields(),
+ ownerName: 'Owner',
+ enabled: true,
+ consentNeeded: consentNeeded,
+ blocked: blocked,
+ openSubmissions: open,
+);
+
+void main() {
+ late CommandTestHarness h;
+ late FakePublicationRepository repo;
+ UuidValue? result;
+ var done = false;
+
+ Future start(WidgetTester tester) async {
+ done = false;
+ await pumpScreen(
+ tester,
+ h,
+ _Starter(
+ onDone: (u) {
+ result = u;
+ done = true;
+ },
+ ),
+ );
+ await tester.tap(_key('start'));
+ await tester.pumpAndSettle();
+ }
+
+ setUp(() {
+ repo = FakePublicationRepository();
+ h = CommandTestHarness(publications: repo);
+ });
+ tearDown(() => h.dispose());
+
+ group('Given a contributor link of a verified publication', () {
+ testWidgets('when consent is needed then the dialog explains what is '
+ 'shared and starting creates the story with consent', (tester) async {
+ repo.card = _card();
+ await start(tester);
+
+ expect(find.textContaining('Serverpod blog receives'), findsOneWidget);
+ expect(find.textContaining('blog.example.com ✓'), findsOneWidget);
+ await tester.tap(find.text('Start writing'));
+ await tester.pumpAndSettle();
+
+ expect(done, isTrue);
+ expect(result, repo.startedItem);
+ expect(repo.calls, contains('startSubmission:agreed'));
+ });
+
+ testWidgets('when consent was given and nothing is open then the story '
+ 'starts without a dialog', (tester) async {
+ repo.card = _card(consentNeeded: false);
+ await start(tester);
+
+ expect(_key('dialog.publicationStart'), findsNothing);
+ expect(result, repo.startedItem);
+ expect(repo.calls, contains('startSubmission'));
+ });
+
+ testWidgets('when there are terms and they are not agreed to then '
+ 'nothing starts', (tester) async {
+ repo.card = _card(termsUrl: 'https://blog.example.com/terms');
+ await start(tester);
+
+ await tester.tap(find.text('Start writing'));
+ await tester.pumpAndSettle();
+
+ expect(result, isNull);
+ expect(repo.calls, isNot(contains('startSubmission:agreed')));
+ });
+
+ testWidgets('when a draft is open then it is offered and continuing '
+ 'opens it without creating', (tester) async {
+ final draft = UuidValue.fromString(
+ '01900000-0000-7000-8000-0000000000d1',
+ );
+ repo.card = _card(
+ consentNeeded: false,
+ open: [
+ PublicationOpenSubmission(
+ itemUuid: draft,
+ name: 'My draft',
+ status: SubmissionStatus.draft,
+ updatedAt: DateTime.utc(2026, 10, 19),
+ ),
+ ],
+ );
+ await start(tester);
+
+ await tester.tap(find.text('Start writing'));
+ await tester.pumpAndSettle();
+
+ expect(result, draft);
+ expect(repo.calls, isNot(contains('startSubmission')));
+ });
+ });
+
+ group('Given an unverified publication', () {
+ testWidgets('then the dialog names the webhook host', (tester) async {
+ repo.card = _card(verified: false);
+ await start(tester);
+
+ expect(find.textContaining('hooks.example.net'), findsOneWidget);
+ expect(find.textContaining('hasn\'t verified'), findsOneWidget);
+ });
+ });
+
+ group('Given a publication that blocked the contributor', () {
+ testWidgets('then it says so and nothing starts', (tester) async {
+ repo.card = _card(blocked: true);
+ await start(tester);
+
+ expect(
+ find.textContaining('isn\'t accepting stories from you'),
+ findsOneWidget,
+ );
+ await tester.tap(find.text('OK'));
+ await tester.pumpAndSettle();
+ expect(result, isNull);
+ expect(repo.calls, ['resolve']);
+ });
+ });
+
+ test('when a /new query has a publication then the request carries it', () {
+ final r = NewItemRequest.fromQuery({
+ 'publication': 'pub_${'A' * 22}',
+ 'ref': 'cfp',
+ });
+
+ expect(r.publicationId, 'pub_${'A' * 22}');
+ expect(r.ref, 'cfp');
+ expect(r.path, '/new?publication=pub_${'A' * 22}&ref=cfp');
+ });
+}
diff --git a/specs/03-app-shell-and-navigation.md b/specs/03-app-shell-and-navigation.md
index 9e2a2a95..84247cae 100644
--- a/specs/03-app-shell-and-navigation.md
+++ b/specs/03-app-shell-and-navigation.md
@@ -65,7 +65,7 @@ exception and must reuse the presentation-session state model.
| `/shared`, `/starred`, `/recent`, `/offline` (app homes), `/trash` | Drive views |
| `/search?q=…` | Search results |
| `/templates` | Template gallery |
-| `/new?folder=&template=` (Write, Sheets, Slides; v2) | Creates an item of the app's kind (offline-first where supported) and replaces the route with its editor; used by Drive's *New* menu and *From template* |
+| `/new?folder=&template=` (Write, Sheets, Slides; v2) | Creates an item of the app's kind (offline-first where supported) and replaces the route with its editor; used by Drive's *New* menu and *From template*. (M14) Write: `/new?publication=pub_…&ref=` starts a story for a publication (online; consent dialog; [18 §3](18-publications-and-submissions.md)) |
| `/import?file=` (Write, Sheets; v2) | Imports an uploaded Drive file with the app's import dialog (Drive 02 §7.2) |
| `/settings/:section` | Wonky Work settings (suite sections, 04 §4) — (v2) |
| `/settings/app`, `/settings/app/:section` | The current app's settings (04 §4) — (v2) |
diff --git a/specs/06-sharing-and-permissions.md b/specs/06-sharing-and-permissions.md
index af441ecc..a258bb9e 100644
--- a/specs/06-sharing-and-permissions.md
+++ b/specs/06-sharing-and-permissions.md
@@ -47,6 +47,7 @@ fields:
grantedById: int
grantedAt: DateTime
expiresAt: DateTime? # optional expiring access (Editors+ can set for others)
+ viaPublicationId: int? # (M14) moderator grant of a submission (18 §5.1); removed with it
indexes:
perm_item_user_idx: { fields: itemId, userId, inheritedFromId, unique: true }
perm_user_idx: { fields: userId }
@@ -148,6 +149,9 @@ the new owner's storage — checked at request and at acceptance, [Drive 03 §7.
Sharing changes are **not undoable**; destructive ones (remove, transfer) confirm.
+(M14) Submitting a document to a publication grants its moderators Commenter without a
+notification; the dialog shows them as "Moderator · " ([18 §5.1](18-publications-and-submissions.md)).
+
## 5. Share dialog (shared UI, `wonky_app`)
```
diff --git a/specs/07-collaboration.md b/specs/07-collaboration.md
index 9a7fbee0..2cb906b1 100644
--- a/specs/07-collaboration.md
+++ b/specs/07-collaboration.md
@@ -96,6 +96,7 @@ fields:
threadId: int, relation(parent=comment_thread, onDelete=Cascade)
authorId: int
authorKind: ActorKind
+ publicationId: int? # (M14) set on a publication's messages (authorKind: system), 18 §5.2
body: String # Markdown (inline subset: bold, italic, code, links, mentions)
mentions: List
reactions: String # JSON {emoji: [userId]}
@@ -171,6 +172,7 @@ fields:
kind: NotificationKind # mention, reply, assigned, shared, accessRequest, suggestion, scriptFailure,
# (v2) storage (quota warnings and the one-time v2 notice, Drive 03 §9);
# (M12) boardReply, boardStatus, boardClaim (16 §12);
+ # (M14) submission (18 §5.3);
# Slides adds kinds only through Slides 09
itemId: int?
payload: String # JSON
@@ -191,7 +193,9 @@ fields:
wrote or voted on was planned, built, shipped, declined or merged, or my post was held or
removed, `boardClaim` claim warnings, expiry and release or PR-closed notices; default
in-app + digest email; links `/drive/improvements/p/` and `/t/`,
- [16 §12](16-bugs-and-improvements.md)) — plus
+ [16 §12](16-bugs-and-improvements.md)), (M14) `submissions` (status changes and messages from a
+ publication on documents I can edit; default in-app + immediate email,
+ [18 §5.3](18-publications-and-submissions.md)) — plus
`digest: bool` (default true). `off` creates no notification.
- Email: **mentions and assignments** set to email are sent right away (a `FutureCall` without
delay, `NotificationEmailFutureCall`); everything else set to email goes into the **digest
diff --git a/specs/08-commands-and-undo.md b/specs/08-commands-and-undo.md
index edd7422c..11855dff 100644
--- a/specs/08-commands-and-undo.md
+++ b/specs/08-commands-and-undo.md
@@ -141,7 +141,7 @@ any), the current selection, the user's role on the document, the `UndoService`,
| Area | Undoable | Not undoable (by design) |
|------|----------|--------------------------|
| Write content | All text/structure/format edits, paste, find-replace-all, AI insertions | — |
-| Write metadata | Title rename, theme assignment, theme edits, page setup, document language | Sharing changes |
+| Write metadata | Title rename, theme assignment, theme edits, page setup, document language, (M14) disconnecting a submission | Sharing changes, (M14) submitting and withdrawing a submission (sent outside Wonky; 18 §4.4) |
| Sheets content | Cell values, formulas, formats, row/col ops, sheet ops, merges, CF/DV rules, filters, sorts, charts, pivots, named ranges, protected ranges | — |
| Sheets settings | Spreadsheet settings (locale, recalc, iterative calc), sheet properties (name, color, gridlines, frozen panes) | — |
| Slides content (v2) | Slides, groups, elements, text, styles, builds, transitions, themes/masters/layouts, code states, chart links (update/unlink), notes — through `slides.rows` | — |
diff --git a/specs/13-security-and-operations.md b/specs/13-security-and-operations.md
index 4e57cf23..c4a55fce 100644
--- a/specs/13-security-and-operations.md
+++ b/specs/13-security-and-operations.md
@@ -26,6 +26,7 @@
| (M12) Prompt injection into triage, task prompts and PR reviews | Jev returns typed answers only; Gemini gets post and PR text as delimited untrusted data and answers through schemas; task prompts need admin review and sit in a fixed template; PR reviews are advisory and the server never runs PR code (16 §10.2, §13.5) |
| (M12) Forged GitHub webhooks, claim takeover | HMAC-SHA256 check (`githubWebhookSecret`), delivery dedupe, per-claim codes compared in constant time (16 §13.3, §13.7) |
| (M12) Runaway AI spend on the board | Separate daily token budgets for Jev and Gemini, per-PR review limits, per-user rate limits (16 §6.1, §11, §13.5) |
+| (M14) Publications: forged contributor links, SSRF through webhook URLs, forged or replayed deliveries, stolen API keys, moderator overreach, submission spam | Links carry only a public publication id and the consent dialog names the verified site host (or the webhook host); outbound calls through the SSRF-safe client; Standard Webhooks signatures with timestamp window and ids; hashed, scoped, rotatable API keys with rate limits; moderators get Commenter only, removed with the submission; per-user limits and blocks ([18 §11](18-publications-and-submissions.md)) |
| Session token theft via XSS (web) | Cookie auth: tokens in an httpOnly, `secure`, `SameSite=lax` cookie shared by both apps; `allowedOrigins` restricts credentialed cross-origin calls (01 §2.5) |
## 2. Privacy
@@ -47,6 +48,9 @@
deck's ops (cell values before and after, authors) are kept 30 days, then pruned by
`revlog.pruneOps`; a slim row (revision, client op id, author, time) stays 365 days for
idempotent resends. Purging a file deletes its ops.
+- (M14) Submissions send a document's text, images, fields and the submitter's display name to
+ the publication the user chose, after a consent dialog; publication signing secrets are stored
+ unencrypted because they must sign ([18 §11](18-publications-and-submissions.md)).
- Export of all user data (*Settings › Account › Download your data*: zip of documents as
Markdown, spreadsheets as XLSX, comments as JSON) — P1.
diff --git a/specs/14-roadmap.md b/specs/14-roadmap.md
index e1d8c233..1fe2fd96 100644
--- a/specs/14-roadmap.md
+++ b/specs/14-roadmap.md
@@ -251,6 +251,17 @@ when the cache can't be opened and before *Clear local data* (15), the `cascaded
(P2, 17 part 6 §6), and character-level merge of concurrent text edits in Slides
([Slides 02 §4.5](wonky_slides/02-data-model.md)).
+### 2.1c Publications & submissions (M14, built)
+
+| Milestone | Goal | Exit criteria |
+|-----------|------|---------------|
+| **M14 · Publications** ([18](18-publications-and-submissions.md), PUB-1…PUB-8) | Blogs register a publication and link to Wonky Write; contributors write and *Submit* to the blog's review queue; moderators comment in the document; the blog's decisions and messages come back as a feedback thread and notifications | J-P1 green against a fake blog receiver; API access matrix and transitions tested; signing vectors and SSRF matrix green; light/dark goldens of the consent dialog, submit dialog, chip and settings section; developer guide verified by building the sample receiver |
+
+**M14 status** (2026-10-20): built, see [18 §13](18-publications-and-submissions.md). Server
+suites (flow, API over HTTP, owner endpoints, rules, the guide's signing vector) and client
+suites (start flow, settings section, Write title row, submit, disconnect undo) are green.
+Open: J-P1 E2E, goldens, a walkthrough against a live server.
+
### 2.2 Executing the plan with parallel agents (v2)
The app plans list **work packages** (Drive 05 §4: D-1…D-18; Slides 12 §4: SL-1…SL-45). Each
diff --git a/specs/18-publications-and-submissions.md b/specs/18-publications-and-submissions.md
new file mode 100644
index 00000000..9749c4c9
--- /dev/null
+++ b/specs/18-publications-and-submissions.md
@@ -0,0 +1,440 @@
+# 18 · Publications and submissions (M14, built)
+
+Lets a blog (or any site with an editorial queue) take stories written in Wonky Write. The blog
+links to Wonky Write; the contributor writes in a normal Wonky document and presses **Submit**;
+the story lands in the blog's review queue, where a moderator approves it, asks for changes or
+declines it. Moderators read and comment on the document in Wonky, and their decisions come
+back into the document as messages.
+
+Status: **built** (M14); open items in §13. The developer
+guide for blog owners is [`docs/blog-integration.md`](../docs/blog-integration.md). As-built
+notes are in §13.
+
+## 1. Decisions
+
+| # | Decision | Reason |
+|---|----------|--------|
+| P1 | The blog registers a **publication** in Wonky once; the contributor link carries only its public id (`/write/new?publication=pub_…`) | The link can't choose where content goes, and holds no secret. A static link works on a static site |
+| P2 | No secret ever lives in a link or a document. The signing secret and the API key stay on the server and with the blog | Sharing, copying, exporting, versions, MCP and AI can't leak them |
+| P3 | Any editor of the document can submit | The blog's moderators are the gate; the worst a wrong submission does is add an entry to the queue |
+| P4 | The action is called **Submit** (not *Publish*) | It goes to review, not live |
+| P5 | Wonky pushes each submission to the blog's webhook, signed per [Standard Webhooks](https://www.standardwebhooks.com/); the blog calls Wonky's publication API back with a status and messages | Small amount of code on the blog side; signatures and timestamps make forged or replayed deliveries detectable |
+| P6 | Each moderator account gets **Commenter** on a submitted document (06 §1: read, comment, suggest), and loses it when the submission is withdrawn, declined or disconnected | Inline comments and suggestions are the best way to give feedback on writing |
+| P7 | The webhook and all outbound calls go through the SSRF-safe client of the URL fetch proxy (13 §1) | The webhook URL is user-supplied |
+| P8 | A submit names a version (07 §5) and records the revision; the blog approves the revision it received | Moderators approve exactly what they reviewed; authors and moderators see diffs between submissions in version history |
+
+## 2. Concepts
+
+- **Publication**: a registered destination, owned by one Wonky user (the blog owner). It has a
+ name, a site URL, a webhook URL, moderators, submission fields, an optional template and terms
+ link, a signing secret and an API key.
+- **Submission**: the link between one Write document and one publication. A document has at
+ most one submission. It starts as a draft when the contributor starts from the link and
+ becomes *In review* with the first submit.
+- **Submitted revision**: one submit of a submission (the first one or a resubmit): document
+ revision, named version, fields, the images it references.
+- **Feedback thread**: one document-level comment thread per submission (Write 08 §1,
+ `anchorKind: document`) where the publication's messages appear and authors reply.
+
+### 2.1 Submission status
+
+```
+ draft ──submit──▶ inReview ──(blog)──▶ changesRequested ──resubmit──▶ inReview
+ ▲ │ ▲ │
+ │ │ └──────────────── resubmit (changes) ─────────────┘
+ │ ├──(blog)──▶ declined (final; Submit hidden)
+ │ └──(blog)──▶ published ──submit update──▶ inReview (publicUrl kept)
+ inReview / changesRequested ──withdraw──▶ withdrawn ──submit──▶ inReview
+```
+
+`declined` is final for that submission: the contributor can disconnect and start a new one.
+Wonky sets `inReview` itself at a submit (the chip shows *Submitting…* until the blog accepts
+the webhook; if every retry fails the status goes back, §7.2); every other transition comes
+from the blog (§7.3). Withdrawing (moderator access removed) leaves `withdrawn`; submitting
+again is allowed.
+
+## 3. Contributor flow
+
+1. **The blog** links to `https://wonky.work/write/new?publication=pub_7Hk2…`, optionally with
+ `&ref=` (≤ 64 chars, `[A-Za-z0-9_-]`, e.g. a call-for-posts id; passed back
+ in every webhook, never trusted for anything).
+2. **Sign in or sign up** if needed: the auth guard keeps the whole route with its query in
+ `next` (03 §2). Sign-up still creates the welcome items (04 §1.1) but lands on `next`.
+3. **`/new` with `publication`** (online only; offline it shows "Starting a story for a
+ publication needs a connection" with *Try again*):
+ - `publication.resolve(publicId)` returns the public card (§9.1) and the caller's open
+ submissions for it (`draft` or `changesRequested`, role ≥ editor). Unknown, disabled or
+ blocked: an empty state "This link doesn't work anymore" (blocked: " isn't accepting
+ stories from you") with *Go to Wonky Write*.
+ - **Consent dialog**, once per user and publication (repeated when the publication's site
+ host, webhook host or terms URL changes):
+ ```
+ ┌ Write for Serverpod blog ─────────────────────────────────┐
+ │ 📰 Serverpod blog · blog.serverpod.dev ✓ │
+ │ You'll write in your own Wonky document. When you press │
+ │ Submit, Serverpod blog receives: │
+ │ • the text and images of the document │
+ │ • the fields you fill in and your display name │
+ │ and its moderators can read and comment on the document. │
+ │ ☐ I agree to Serverpod blog's contributor terms ↗ │
+ │ [Cancel] [Start writing] │
+ └───────────────────────────────────────────────────────────┘
+ ```
+ Unverified publication (§8.2): the line shows the **webhook host** instead of the site host
+ and "Wonky hasn't verified that this publication belongs to blog.serverpod.dev". The terms
+ checkbox shows only when the publication has a terms URL and is required then. With open
+ submissions the dialog lists them first: *Continue "My story"* rows and *Start a new story*.
+ - *Start writing* calls `publication.startSubmission(publicId, ref?)`: the server creates a
+ Write document in the user's *My files* root (from the publication's template, or empty;
+ counted in the user's quota, Drive 03), the submission (`draft`) and the consent record,
+ and returns the document uuid; the client replaces the route with `/d/:uuid`.
+4. **Writing** is a normal document: co-authors, comments, suggestions, offline editing.
+5. **Submit** (§4.2) sends the current revision. The chip shows **In review**.
+6. **Feedback**: moderators comment and suggest in the document as themselves; the blog's
+ status messages appear in the feedback thread (§5) and notify the document's editors.
+7. **Resubmit / Submit update** after changes; **Withdraw** from the chip's popover.
+
+## 4. Write UI
+
+### 4.1 Title row
+
+Documents with a submission show, left of *Share* (Write 05 §3.2):
+
+| Element | Widget | Behaviour |
+|---------|--------|-----------|
+| Status chip | `SubmissionStatusChip` (`wonky_app`; Lucide `newspaper`) | Hidden for `draft`. *In review* (neutral), *Changes requested* (warning), *Declined* (muted), *Published* (success). Click → status popover (§4.3). Shown to every role |
+| Submit | `ShadButton.secondary` (Lucide `send`) | `write.submission.submit`. Label: *Submit* (`draft`), *Resubmit* (`changesRequested`, or `inReview` with changes), *Submit update* (`published` with changes). Disabled with a tooltip: offline ("Submitting needs a connection"), no changes since the last submitted revision ("No changes since you submitted"). Hidden for role < editor, `declined`, and a disabled publication. Tooltip "Submit to Serverpod blog". Between 640 and 1200 px icon only |
+
+### 4.2 Submit dialog
+
+```
+┌ Submit to Serverpod blog ───────────────────────────────────┐
+│ 📰 Serverpod blog · blog.serverpod.dev ✓ │
+│ Title* [ How we sped up our CI ×3 ] │
+│ Summary [ A short look at … ] │
+│ Tags [ dart ×] [ ci ×] [ + ] │
+│ Cover image [ (thumbnail) Change ] │
+│ Note to the moderators [ ] │
+│ ⚠ 2 open suggestions aren't included. Review them first? │
+│ Comments in the document stay in Wonky (moderators can see │
+│ them). │
+│ [Cancel] [Submit] │
+└─────────────────────────────────────────────────────────────┘
+```
+
+- Fields are the publication's configuration (§8.1). First submit prefills: title = document
+ title (or the first heading), summary = the first paragraph (≤ 300 chars), cover = the first
+ image. Later submits start from the stored values.
+- Open suggestions are sent as the original text (Write 07 §4.1 `suggestions: original`); the
+ warning links to the first one.
+- *Submit* calls `submission.submit(itemUuid, fields, expectedRevision)`; it waits up to 10 s
+ for the delivery (§7.2). Accepted: toast "Submitted to Serverpod blog", chip *In review*. Not
+ accepted in time: chip *Submitting…*, toast "Serverpod blog didn't answer yet. We'll keep
+ trying." (§7.2).
+
+### 4.3 Status popover
+
+Publication card (name, host, ✓), status with time and actor ("In review · submitted by Anna 2
+h ago, revision 3"), the last message (excerpt, *Open thread* scrolls the margin to the feedback
+thread), *View post ↗* (when `publicUrl`), the submission history (submitted revisions with
+*Compare* → version history), *Withdraw submission* (`inReview`, `changesRequested`; confirm),
+*Disconnect from Serverpod blog* (not while `inReview`/`changesRequested`; §4.5).
+
+### 4.4 Menus and commands
+
+File menu (Write 05 §7.1), after *Share ▸*: *Submit to …* (`write.submission.submit`),
+*Submission status* (`write.submission.showStatus`), *Withdraw submission*
+(`write.submission.withdraw`) — only for documents with a submission.
+
+| Command | Undo |
+|---------|------|
+| `write.submission.submit {fields?}` | Not undoable (sent outside Wonky; *Withdraw* is the counterpart) |
+| `write.submission.withdraw` | Not undoable; confirms |
+| `write.submission.disconnect` | Undoable (domain `drive`, server inverse: `reconnect` clears `disconnectedAt`) |
+| `write.submission.showStatus` | — (view) |
+| `shell.settings.open {section: publications}` | — |
+
+`requiredRole: editor` except `showStatus` (viewer). Enabled flags: `online`, `hasSubmission`.
+
+### 4.5 Copies, trash and history
+
+- *Make a copy*, templates and export never take the submission along.
+- Trashing a document withdraws an open submission (`submission.withdrawn`, reason `trashed`);
+ restoring does not resubmit. Purging deletes the submission row (history stays in the blog).
+- Disconnecting sets `disconnectedAt`, removes moderator grants and sends
+ `submission.disconnected`; undo within the session reconnects (no webhook for the
+ reconnect — the next submit carries the same submission id).
+- Version restore doesn't change the submission.
+
+## 5. Moderators and feedback
+
+### 5.1 Moderator access
+
+- Moderators are Wonky accounts the publication owner lists (§8.1; pending emails are converted
+ on sign-up, like invites, 06 §2).
+- **First submit** grants each moderator Commenter on the document: a `DriveItemPermission` with
+ `viaPublicationId` set (§6), `grantedById` = the submitter, no share notification or email. A
+ moderator who already has a role keeps the higher one and gets no extra row.
+- Withdraw, decline, disconnect, trash and removal of a moderator from the publication delete
+ the `viaPublicationId` rows; moderators added later get access to the publication's
+ `inReview`/`changesRequested` submissions. `published` submissions keep access (updates are
+ reviewed the same way).
+- The share dialog (06 §5) shows these rows as "Moderator · Serverpod blog" with Commenter; the
+ owner (or an editor who may share) can remove them. A direct `share`/`updateRole` for that user
+ turns the row into a direct grant (`viaPublicationId` cleared).
+- Moderators open documents from the blog's queue (`documentUrl` in the webhook); submissions
+ don't appear in their *Shared with me* until opened (06 §2.2 behaviour for state rows).
+
+### 5.2 Feedback thread
+
+- Created by the first message from the blog (§7.3): a `document` thread whose comments have
+ `authorKind: system` and `publicationId` set (07 §2.1). They render with the publication's name
+ and the `newspaper` icon. Status messages start with a status line ("**Changes requested**").
+- Editors and moderators reply as themselves. Replies by anyone except the publication itself are
+ sent to the blog as `comment.created` (§7.1). Other threads stay in Wonky.
+- Resolving the thread is allowed; a new message from the blog reopens it.
+
+### 5.3 Notifications
+
+New `NotificationKind.submission` (07 §4) for status changes and blog messages, to every editor
+of the document (and the submitter). New category `submissions` in Settings › Notifications:
+default in-app + immediate email (rare and important). Payload: publication name, status,
+message excerpt; link to the document with `?comment=`.
+
+## 6. Data model (server, `platform/publications/`)
+
+The models are in `wonky_server/lib/src/platform/publications/models/` (all tables
+`serverOnly`; clients get the `*View`, `PublicationCard`, `PublicationSettings` and
+`PublicationSecret` classes):
+
+| Table | What |
+|-------|------|
+| `publication` | Owner, `publicId` (`pub_` + 22), name, site URL (origin), webhook URL, `verifiedHost`, template item, terms URL, `fields` (`PublicationFields`, JSON column), signing secret (+ previous one during a 24 h rotation), API key hash and last 4, enabled |
+| `publication_moderator` | Email (lower case) and the user once an account has it |
+| `publication_consent` | Per user: a hash of what was agreed to (shown host, webhook host, terms URL) |
+| `publication_block` | Users the publication refuses |
+| `submission` | One per document (`itemId` unique): `uuid` (public `sub_`), `ref`, `status`, `statusBeforeSubmit` (restored when a delivery fails), `delivery` (`pending`/`failed`/null), `values` (`SubmissionValues`, JSON), `publicUrl`, `feedbackThreadId`, `disconnectedAt` |
+| `submission_revision` | One per submit: `sequence`, document `revision`, named `versionId`, values, and the **`payload`** sent (served again by `GET …/content`) |
+| `submission_asset` | Images a submit referenced (`fileUuid`); the API key may download only these |
+| `publication_delivery` | The webhook outbox: `webhookId` (`msg_…`), type, payload, `sequence`, attempts, `nextAttemptAt`, `leaseUntil`, last status and error, delivered/failed times |
+
+Changes to existing models: `DriveItemPermission.viaPublicationId` (FK, cascade: deleting a
+publication removes its moderator grants; 06 §2) and `PermissionView.viaPublication` (the
+share dialog's "Moderator · "), `Comment.publicationId` (FK, set null) and
+`CommentView.authorLabel` (07 §2.1), `NotificationKind.submission` (07 §4),
+`ActivityType.submission` (05 §5; summary `{publication, action: submitted | withdrawn |
+disconnected | reconnected | status, status?}`).
+
+## 7. Protocol
+
+### 7.1 Webhooks (Wonky → blog)
+
+`POST `, `Content-Type: application/json`, headers `webhook-id`,
+`webhook-timestamp` (Unix seconds) and `webhook-signature` (`v1,..">`; during rotation two space-separated signatures). The blog must
+check the signature, reject timestamps more than 5 minutes off and drop repeated ids.
+
+| Type | When | Extra fields |
+|------|------|--------------|
+| `submission.submitted` | Every submit | `sequence`, `revision`, `fields`, `markdown`, `assets`, `submittedBy`, `note` |
+| `submission.withdrawn` | Withdraw or trash | `reason`: `author` \| `trashed` |
+| `submission.disconnected` | Disconnect (also after `published`) | — |
+| `comment.created` | Reply in the feedback thread | `comment {id, body (Markdown), author {name, wonkyUserId, isModerator}}` |
+| `publication.ping` | *Send test* and saving a new webhook URL | — |
+
+```json
+{
+ "type": "submission.submitted",
+ "createdAt": "2026-10-20T09:14:03Z",
+ "publicationId": "pub_7Hk2…",
+ "submission": {
+ "id": "sub_0192…", "ref": "cfp-2026", "status": "inReview",
+ "documentUrl": "https://wonky.work/write/d/0192…"
+ },
+ "sequence": 2, "revision": 418,
+ "fields": { "title": "How we sped up our CI ×3", "summary": "…", "tags": ["dart", "ci"],
+ "coverImage": "ast_0192…" },
+ "note": "Fixed the code samples, thanks!",
+ "markdown": "# How we sped up our CI ×3\n\n……",
+ "assets": [{ "id": "ast_0192…", "name": "build-graph.png", "type": "image/png", "bytes": 48213,
+ "url": "https://wonky.work/api/pub/v1/assets/ast_0192…" }],
+ "submittedBy": { "name": "Anna Berg", "wonkyUserId": "0191…" }
+}
+```
+
+- `markdown` is the Write Markdown export (Write 07 §4) with suggestions as original, no front
+ matter and no comments; `wonky-file://` images become asset URLs, `https://` images stay.
+- Asset URLs need the API key (§7.3). The blog should download them when it receives the
+ webhook; they work as long as the image exists in Wonky.
+- No email addresses are sent. `wonkyUserId` is the profile uuid (for blocking, §7.3).
+
+### 7.2 Delivery
+
+- Sent by the server through the SSRF-safe client (P7: resolve, block private/link-local/
+ metadata ranges, pin the address, https on port 443 only, no redirects), 10 s
+ timeout, response body ignored beyond 4 KB. 2xx = accepted.
+- `submission.submit` writes the `PublicationDelivery` row in the same transaction as the
+ `SubmissionRevision`, then tries once while the client waits (≤ 10 s). Accepted →
+ `status = inReview`, `delivery = null`. Otherwise `delivery = pending` and the outbox retries
+ with the same `webhook-id`: 1 min, 5 min, 30 min, 2 h, 6 h, 12 h (`wonky.publicationSweep`,
+ every minute, under a lease; at most 4 concurrent deliveries per publication, oldest first).
+ After the last failure: `delivery = failed`, the status stays where it was (the chip drops *Submitting…*), the
+ submitter gets a `submission` notification ("Serverpod blog couldn't receive your
+ submission"), and the owner sees it under *Deliveries*.
+- Deliveries are delivered at least once and may arrive out of order; `sequence` lets the blog
+ ignore an older submit.
+
+### 7.3 Publication API (blog → Wonky)
+
+Relic routes under `https://wonky.work/api/pub/v1/` (`wonky_server/lib/src/web/routes/`),
+`Authorization: Bearer wpk_…` (the publication's API key; every call is scoped to that
+publication's submissions), JSON, errors `{ "error": "", "message": "…" }`. Header-only
+checks before the body (13 §1), 1 MB body cap, 120 requests/min per key (Redis counter like the
+MCP limit, 13 §4).
+
+| Call | Purpose |
+|------|---------|
+| `GET submissions?status=&after=&limit=` | List for reconciliation (cursor paging) |
+| `GET submissions/{id}` | Status, `documentUrl`, last `sequence`/`revision`, fields, `publicUrl` |
+| `GET submissions/{id}/content?sequence=` | The `submission.submitted` payload of that submit (from its named version), for a missed webhook |
+| `GET assets/{assetId}` | Image bytes; only assets listed in a `SubmissionRevision` of this publication; streamed like downloads (13 §6) |
+| `POST submissions/{id}/status` `{status, message?, publicUrl?}` | `inReview`, `changesRequested`, `declined`, `published` (`publicUrl` required, https). Transitions per §2.1; `409 invalidTransition` otherwise. `message` (Markdown, ≤ 10 000 chars) goes into the feedback thread |
+| `POST submissions/{id}/messages` `{body}` | A message in the feedback thread without a status change |
+| `POST blocks` `{wonkyUserId}` · `DELETE blocks/{wonkyUserId}` | Stop a user from starting or submitting stories to this publication |
+
+A status call or message on a withdrawn or disconnected submission is `410 gone`.
+
+## 8. Publication setup (blog owner)
+
+### 8.1 Settings › Publications
+
+New Wonky Work settings section `publications` (04 §4.1): a list of the user's publications
+(≤ 10) with an empty state "Let people write for your blog in Wonky Write" and *New
+publication*. The editor:
+
+- **General**: name, site URL, webhook URL, enabled, terms URL, template (picker of the owner's
+ Write documents; its content is copied at start, comments not).
+- **Submission fields**: title (always, required) plus toggles for summary, tags (optional list
+ of allowed tags), cover image, author bio, note to the moderators; each optional or required.
+- **Moderators**: add by email (≤ 20), remove.
+- **Verification** (§8.2): status and *Verify*.
+- **Credentials**: publication id and contributor link (copy buttons), signing secret (shown once
+ at creation; *Rotate*, old one signs for 24 h), API key (*Create*, shown once; *Rotate*
+ revokes the old key at once).
+- **Deliveries**: the last 50 with type, time, HTTP status, error; *Resend*; *Send test*
+ (`publication.ping`).
+
+Saving a new webhook URL sends a `publication.ping`; a non-2xx answer shows the error and saves
+the URL anyway (the blog may deploy its receiver later). Settings changes are not undoable
+(like sharing, 08 §2).
+
+### 8.2 Verification
+
+The owner serves `https:///.well-known/wonky-publications.json`:
+
+```json
+{ "publications": ["pub_7Hk2…"] }
+```
+
+*Verify* fetches it through the SSRF-safe client; when it lists the publication,
+`verifiedHost` = the site host. Changing the site URL clears verification. Verified publications
+show the site host with ✓ in the consent and submit dialogs; unverified ones show the webhook
+host and a warning (§3), so a copied name can't pretend to be another blog.
+
+### 8.3 Blog-side checklist (also in the developer guide, §12 PUB-7)
+
+1. Create the publication, verify the site, store the signing secret and API key server-side.
+2. Add the *Write for us* link.
+3. Webhook receiver: check the signature (any Standard Webhooks library), dedupe by
+ `webhook-id`, upsert the queue entry by `submission.id`, ignore an older `sequence`, download
+ the assets, answer 2xx fast.
+4. Moderation UI: show the Markdown, link *Open in Wonky* (`documentUrl`), and call the status
+ API for *Request changes*, *Decline* and *Publish* (with the post's URL).
+
+## 9. Endpoints and MCP
+
+### 9.1 `PublicationEndpoint` (`publication`, signed in)
+
+`resolve(publicId)` → `PublicationCard {name, siteHost, verified, webhookHost, termsUrl,
+fields, ownerName, consentNeeded, openSubmissions}`; `startSubmission(publicId, ref?,
+consent?)` → item uuid; owner-only: `list()`, `create(...)`, `update(...)`, `verify(uuid)`,
+`rotateSecret(uuid)`, `createApiKey(uuid)` → key once, `setModerators(uuid, emails)`,
+`deliveries(uuid)`, `resend(deliveryId)`, `sendTest(uuid)`, `delete(uuid)` (disconnects every
+submission, sends nothing).
+
+### 9.2 `SubmissionEndpoint` (`submission`)
+
+`get(itemUuid)` → `SubmissionView?` (viewer+); `submit(itemUuid, values, agreed)` (editor+,
+publication enabled, not blocked, not `declined`; `agreed` confirms changed terms): names a
+version, exports that version's revision and sends it; `withdraw(itemUuid)`,
+`disconnect(itemUuid)`, `reconnect(itemUuid)` (editor+). Changes reach open editors as a
+`DriveChange` of kind `updated` for the item; Write's `SubmissionController` reloads on it.
+
+### 9.3 MCP and AI
+
+**Not planned** (only on a feature request): headless `write.get_submission` and
+`write.submit` tools. Agents can use the commands through the UI bridge
+(`ui.invoke_command`, `write.submission.*`). The AI assistant can't submit, withdraw or
+disconnect (like sharing, 13 §1).
+
+## 10. Limits and abuse
+
+- ≤ 10 publications per owner, ≤ 20 moderators per publication.
+- Per user and publication: ≤ 10 `startSubmission` and ≤ 20 submits per day
+ (`limitExceeded` "You've sent a lot to Serverpod blog today. Try again tomorrow.").
+- Markdown payload ≤ 5 MB (`tooLarge` in the submit dialog); fields ≤ 2 000 chars each.
+- Blocks (§7.3) stop `startSubmission` and `submit` for that user.
+- Account deletion: an owner's publications are deleted (submissions disconnected, moderator
+ grants removed); a contributor's documents follow 13 §2 and their open submissions are
+ withdrawn.
+
+## 11. Security and privacy summary
+
+Rows in 13 §1: forged contributor links (P1, §8.2), SSRF through the webhook URL (P7), forged or
+replayed deliveries (signatures, timestamp window, ids), stolen API keys (hashed, shown once,
+rotate, scoped to one publication, rate limited), moderator access beyond the submission
+(Commenter only, removed on withdraw/decline/disconnect), spam and floods (§10). Privacy (13 §2):
+submitted content goes to a third party the user chose, after consent; the signing secret is
+stored unencrypted (it must sign) with the same caveat as user AI keys; the privacy policy (13
+§5) names submissions.
+
+## 12. Work packages
+
+| WP | Scope | Depends on |
+|----|-------|------------|
+| PUB-1 | Models (§6) and migration, `PublicationService`, `SubmissionService`, access rules, moderator grants (06), feedback-thread comments (07) | — |
+| PUB-2 | Outbound: shared SSRF-safe client extracted from `url_fetch_proxy.dart`, Standard Webhooks signer, outbox and `wonky.publicationSweep` | PUB-1 |
+| PUB-3 | Publication API routes (§7.3), API keys, rate limit, `.well-known` verification | PUB-1 |
+| PUB-4 | Settings › Publications (§8.1) | PUB-1–3 |
+| PUB-5 | Write: `/new?publication=`, consent dialog, Submit button and dialog, status chip and popover, commands, File menu, notifications category | PUB-1, PUB-2 |
+| PUB-6 | MCP tools, activity entries, legal pages | PUB-5 |
+| PUB-7 | Developer guide for blog owners with a sample receiver (Dart, Relic) | PUB-3 |
+| PUB-8 | Tests: services and API (access matrix, transitions, rotation, blocks), webhook signing vectors, SSRF matrix reused, delivery retries with a fake receiver (tests allow a loopback receiver through a test-only override, never in staging/production), widget tests and goldens (light/dark), E2E journey J-P1: link → sign up → write → submit → request changes → resubmit → publish | all |
+
+## 13. As built (M14)
+
+- **Server**: `platform/publications/` (`PublicationService`, `SubmissionService`,
+ `PublicationWebhooks` outbox, `PublicationApi` behind `web/routes/publication_api_route.dart`,
+ `PublicationHttp` on the SSRF-safe `UrlFetchProxy` with https/443 only and no redirects,
+ `PublicationsFutureCall` with `wonky.publicationSweep` every minute, deliveries purged after 30
+ days). Hooks: trash/restore (`DriveTrash`), feedback-thread replies (`CommentService.reply`),
+ pending moderators linked at sign-up (`UserService.ensureProfile`), direct shares turn a
+ moderator grant into a direct one (`SharingService`).
+- **Moderator access** is synced at every submit, status change, withdraw, disconnect, trash
+ and restore. A moderator who signs up later, or whom an editor removes in the share dialog,
+ gets access back with the next submit or status change.
+- **Templates**: the template's text is copied without comments and with suggestions accepted;
+ its images are copied into the new document (they belong to the contributor).
+- **Client**: `wonky_app` (`PublicationRepository`, Settings › Publications
+ `PublicationsSection`, `startPublicationStory` for `/new?publication=`, `SubmissionUndoHandler`
+ for disconnect, notification, activity, comment and share-dialog rendering) and Write
+ (`features/publish/`: `SubmissionController` on `WriteDocumentController.submission`,
+ `SubmissionTitleActions`, the submit dialog, `write.submission.*` commands, File menu items,
+ which stay listed but disabled for documents without a submission).
+- **Tests**: server `test/unit/publications/` (signing vector of the guide, rules) and
+ `test/integration/platform/publications/` (flow, API over HTTP, owner endpoints); client
+ `wonky_app/test/publications/` and `wonky_write/test/features/publish/`.
+- **Open**: light/dark goldens of the dialogs and chip; the E2E journey J-P1; moderators who
+ sign up later get access only with the next submit or status change.
+- **Not planned** (only on a feature request): MCP tools (§9.3); Write `?comment=` deep links,
+ so notifications open the publication's thread (they open the document).
diff --git a/specs/README.md b/specs/README.md
index 1868950b..4e8ba85e 100644
--- a/specs/README.md
+++ b/specs/README.md
@@ -67,10 +67,11 @@ charts).
| 11 | [Quality](11-quality-testing-accessibility.md) | Testing, performance budgets, accessibility, i18n, observability |
| 12 | [Offline & sync](12-offline-and-sync.md) | Local stores, Write offline, Sheets/Slides on RevLog (summary), upload queue, scale limits |
| 13 | [Security & operations](13-security-and-operations.md) | Threat model, privacy, deployment, scheduled jobs, arbitrary uploads |
-| 14 | [Roadmap](14-roadmap.md) | Milestones M0–M13 (v2: M8 Drive + suite changes, M9–M11 Slides; M12 Bugs & improvements; M13 RevLog), how parallel agents execute the work packages, shared workstreams, risks |
+| 14 | [Roadmap](14-roadmap.md) | Milestones M0–M14 (v2: M8 Drive + suite changes, M9–M11 Slides; M12 Bugs & improvements; M13 RevLog; M14 Publications), how parallel agents execute the work packages, shared workstreams, risks |
| 15 | [Writing tests](15-writing-tests.md) | How to write tests: layout, naming, helpers, server/widget/golden/property/E2E patterns, determinism, running |
| 16 | [Bugs & improvements](16-bugs-and-improvements.md) | Public board for bug reports and improvement ideas: report dialog with annotated screenshots, votes, comments, AI triage and moderation, admins, tasks with contributor prompts, claims, GitHub PR rules (M12, built) |
| 17 | [RevLog sync](17-revlog-sync.md) | Server-sequenced op log of Sheets and Slides: decisions, data model, change sets and apply rules, protocol, server (sequencer, hooks, pages, headless cache), client (cache, outbox, bootstrap, conflicts), performance gates, as-built notes (M13) |
+| 18 | [Publications & submissions](18-publications-and-submissions.md) | Blogs register a publication; contributors write in Wonky Write and *Submit* to the blog's review queue (signed webhooks, publication API, moderators with comment access, feedback in the document) (M14, built) |
## Conventions used in all specs
diff --git a/specs/wonky_write/05-screens-menus-toolbar.md b/specs/wonky_write/05-screens-menus-toolbar.md
index 36787b12..b927d9fa 100644
--- a/specs/wonky_write/05-screens-menus-toolbar.md
+++ b/specs/wonky_write/05-screens-menus-toolbar.md
@@ -87,6 +87,7 @@ Overlays (dialogs, palette, toasts, offline banner, MCP pill) per suite 03 §3.
| — spacer — | | |
| Presence | `PresenceAvatarStack` | Click avatar → `write.nav.goToCollaborator`; ⋮ → Follow (`write.nav.follow`) |
| Bugs & improvements (M12) | `ShadIconButton.ghost` (Lucide `messageSquarePlus`, tooltip "Bugs & improvements"; replaces *Report a bug*) | `shell.board.report` — report dialog, suite 16 §4.2; hidden below 640 px (still in Help) |
+| Submission status, Submit (M14) | `SubmissionStatusChip`, `ShadButton.secondary` (Lucide `send`) | Only for documents with a submission: status popover, `write.submission.submit` — [18 §4](../18-publications-and-submissions.md) |
| Share | `ShadButton` primary (lock / globe / users icon by link state) | `shell.sharing.open` |
| Account | `ShadAvatar` menu | suite 04; (v2) *Write settings*, *Wonky Work settings*, *Open ▸* the four apps, *Sign out* |
@@ -263,6 +264,7 @@ Write behaviour noted.
| ─ | | | | |
| Share ▸ Share with others *(shell)* | `shell.sharing.open` | — | — | always (role decides dialog content) |
| Share ▸ Copy link *(shell)* | `shell.sharing.copyLink` | — | — | always |
+| Submit to … (M14) | `write.submission.submit` (+ *Submission status*, *Withdraw submission*; [18 §4.4](../18-publications-and-submissions.md)) | — | — | has a submission, role ≥ editor, `online` |
| Download ▸ Markdown (.md) | `write.file.download {format: md}` | — | — | `copyAllowed` |
| Download ▸ Markdown with images (.zip) | `{format: mdZip}` | — | — | same |
| Download ▸ PDF (.pdf) | `{format: pdf}` | — | — | same |
diff --git a/wonky_client/lib/src/protocol/client.dart b/wonky_client/lib/src/protocol/client.dart
index 7fcde455..2031470f 100644
--- a/wonky_client/lib/src/protocol/client.dart
+++ b/wonky_client/lib/src/protocol/client.dart
@@ -150,6 +150,24 @@ import 'package:wonky_client/src/protocol/platform/presence/presence_event.dart'
as _i1lme7np;
import 'package:wonky_client/src/protocol/platform/presence/presence_update.dart'
as _izu9ijhe;
+import 'package:wonky_client/src/protocol/platform/publications/models/publication_card.dart'
+ as _igbz1wj9;
+import 'package:wonky_client/src/protocol/platform/publications/models/publication_check_result.dart'
+ as _i9sbxmp8;
+import 'package:wonky_client/src/protocol/platform/publications/models/publication_delivery_view.dart'
+ as _ipl2ldhs;
+import 'package:wonky_client/src/protocol/platform/publications/models/publication_input.dart'
+ as _ii8xbzwt;
+import 'package:wonky_client/src/protocol/platform/publications/models/publication_secret.dart'
+ as _ihfocp8h;
+import 'package:wonky_client/src/protocol/platform/publications/models/publication_settings.dart'
+ as _i9bruko9;
+import 'package:wonky_client/src/protocol/platform/publications/models/submission_started.dart'
+ as _i767e3eb;
+import 'package:wonky_client/src/protocol/platform/publications/models/submission_values.dart'
+ as _i8t9b0m8;
+import 'package:wonky_client/src/protocol/platform/publications/models/submission_view.dart'
+ as _iy9ei8t0;
import 'package:wonky_client/src/protocol/platform/revlog/messages/rev_client_message.dart'
as _iedvhh7l;
import 'package:wonky_client/src/protocol/platform/revlog/messages/rev_server_message.dart'
@@ -2257,6 +2275,189 @@ class EndpointPresence extends _isc.EndpointRef {
);
}
+/// Publications (specs/18 §9.1): starting a story from a contributor link,
+/// and the owner's settings.
+/// {@category Endpoint}
+class EndpointPublication extends _isc.EndpointRef {
+ EndpointPublication(_isc.EndpointCaller caller) : super(caller);
+
+ @override
+ String get name => 'publication';
+
+ /// The publication of a contributor link.
+ _ida.Future<_igbz1wj9.PublicationCard> resolve(String publicId) =>
+ caller.callServerEndpoint<_igbz1wj9.PublicationCard>(
+ 'publication',
+ 'resolve',
+ {'publicId': publicId},
+ );
+
+ /// Creates the document of a new story; [agreed] confirms the consent
+ /// dialog.
+ _ida.Future<_i767e3eb.SubmissionStarted> startSubmission(
+ String publicId,
+ String? ref,
+ bool agreed,
+ ) => caller.callServerEndpoint<_i767e3eb.SubmissionStarted>(
+ 'publication',
+ 'startSubmission',
+ {
+ 'publicId': publicId,
+ 'ref': ref,
+ 'agreed': agreed,
+ },
+ );
+
+ _ida.Future> list() =>
+ caller.callServerEndpoint>(
+ 'publication',
+ 'list',
+ {},
+ );
+
+ /// Creates a publication; the result holds the signing secret once.
+ _ida.Future<_ihfocp8h.PublicationSecret> create(
+ _ii8xbzwt.PublicationInput input,
+ ) => caller.callServerEndpoint<_ihfocp8h.PublicationSecret>(
+ 'publication',
+ 'create',
+ {'input': input},
+ );
+
+ _ida.Future<_i9bruko9.PublicationSettings> update(
+ String publicId,
+ _ii8xbzwt.PublicationInput input,
+ ) => caller.callServerEndpoint<_i9bruko9.PublicationSettings>(
+ 'publication',
+ 'update',
+ {
+ 'publicId': publicId,
+ 'input': input,
+ },
+ );
+
+ _ida.Future delete(String publicId) => caller.callServerEndpoint(
+ 'publication',
+ 'delete',
+ {'publicId': publicId},
+ );
+
+ _ida.Future<_i9sbxmp8.PublicationCheckResult> verify(String publicId) =>
+ caller.callServerEndpoint<_i9sbxmp8.PublicationCheckResult>(
+ 'publication',
+ 'verify',
+ {'publicId': publicId},
+ );
+
+ _ida.Future<_ihfocp8h.PublicationSecret> rotateSecret(String publicId) =>
+ caller.callServerEndpoint<_ihfocp8h.PublicationSecret>(
+ 'publication',
+ 'rotateSecret',
+ {'publicId': publicId},
+ );
+
+ _ida.Future<_ihfocp8h.PublicationSecret> createApiKey(String publicId) =>
+ caller.callServerEndpoint<_ihfocp8h.PublicationSecret>(
+ 'publication',
+ 'createApiKey',
+ {'publicId': publicId},
+ );
+
+ _ida.Future<_i9bruko9.PublicationSettings> setModerators(
+ String publicId,
+ List emails,
+ ) => caller.callServerEndpoint<_i9bruko9.PublicationSettings>(
+ 'publication',
+ 'setModerators',
+ {
+ 'publicId': publicId,
+ 'emails': emails,
+ },
+ );
+
+ _ida.Future> deliveries(
+ String publicId,
+ ) => caller.callServerEndpoint>(
+ 'publication',
+ 'deliveries',
+ {'publicId': publicId},
+ );
+
+ _ida.Future<_ipl2ldhs.PublicationDeliveryView> resend(
+ String publicId,
+ String webhookId,
+ ) => caller.callServerEndpoint<_ipl2ldhs.PublicationDeliveryView>(
+ 'publication',
+ 'resend',
+ {
+ 'publicId': publicId,
+ 'webhookId': webhookId,
+ },
+ );
+
+ _ida.Future<_i9sbxmp8.PublicationCheckResult> sendTest(String publicId) =>
+ caller.callServerEndpoint<_i9sbxmp8.PublicationCheckResult>(
+ 'publication',
+ 'sendTest',
+ {'publicId': publicId},
+ );
+}
+
+/// A document's submission to a publication (specs/18 §9.2).
+/// {@category Endpoint}
+class EndpointSubmission extends _isc.EndpointRef {
+ EndpointSubmission(_isc.EndpointCaller caller) : super(caller);
+
+ @override
+ String get name => 'submission';
+
+ /// The document's submission, or null (viewer+).
+ _ida.Future<_iy9ei8t0.SubmissionView?> get(_isc.UuidValue itemUuid) =>
+ caller.callServerEndpoint<_iy9ei8t0.SubmissionView?>(
+ 'submission',
+ 'get',
+ {'itemUuid': itemUuid},
+ );
+
+ /// Submits the current revision (editor+); [agreed] confirms the
+ /// publication's terms when they changed.
+ _ida.Future<_iy9ei8t0.SubmissionView> submit(
+ _isc.UuidValue itemUuid,
+ _i8t9b0m8.SubmissionValues values,
+ bool agreed,
+ ) => caller.callServerEndpoint<_iy9ei8t0.SubmissionView>(
+ 'submission',
+ 'submit',
+ {
+ 'itemUuid': itemUuid,
+ 'values': values,
+ 'agreed': agreed,
+ },
+ );
+
+ _ida.Future<_iy9ei8t0.SubmissionView> withdraw(_isc.UuidValue itemUuid) =>
+ caller.callServerEndpoint<_iy9ei8t0.SubmissionView>(
+ 'submission',
+ 'withdraw',
+ {'itemUuid': itemUuid},
+ );
+
+ _ida.Future disconnect(_isc.UuidValue itemUuid) =>
+ caller.callServerEndpoint(
+ 'submission',
+ 'disconnect',
+ {'itemUuid': itemUuid},
+ );
+
+ /// Undoes [disconnect].
+ _ida.Future<_iy9ei8t0.SubmissionView?> reconnect(_isc.UuidValue itemUuid) =>
+ caller.callServerEndpoint<_iy9ei8t0.SubmissionView?>(
+ 'submission',
+ 'reconnect',
+ {'itemUuid': itemUuid},
+ );
+}
+
/// Sharing (specs/06 §4).
/// {@category Endpoint}
class EndpointSharing extends _isc.EndpointRef {
@@ -4243,6 +4444,8 @@ class Client extends _isc.ServerpodClientShared {
meta = EndpointMeta(this);
notification = EndpointNotification(this);
presence = EndpointPresence(this);
+ publication = EndpointPublication(this);
+ submission = EndpointSubmission(this);
sharing = EndpointSharing(this);
accessToken = EndpointAccessToken(this);
googleIdp = EndpointGoogleIdp(this);
@@ -4297,6 +4500,10 @@ class Client extends _isc.ServerpodClientShared {
late final EndpointPresence presence;
+ late final EndpointPublication publication;
+
+ late final EndpointSubmission submission;
+
late final EndpointSharing sharing;
late final EndpointAccessToken accessToken;
@@ -4361,6 +4568,8 @@ class Client extends _isc.ServerpodClientShared {
'meta': meta,
'notification': notification,
'presence': presence,
+ 'publication': publication,
+ 'submission': submission,
'sharing': sharing,
'accessToken': accessToken,
'googleIdp': googleIdp,
diff --git a/wonky_client/lib/src/protocol/platform/activity/activity_type.dart b/wonky_client/lib/src/protocol/platform/activity/activity_type.dart
index 159f614f..258938b6 100644
--- a/wonky_client/lib/src/protocol/platform/activity/activity_type.dart
+++ b/wonky_client/lib/src/protocol/platform/activity/activity_type.dart
@@ -26,7 +26,12 @@ enum ActivityType implements _isc.SerializableModel {
exported,
/// (v2) A file was uploaded (Drive 02 §3.3).
- uploaded;
+ uploaded,
+
+ /// (M14) Submitted to, withdrawn from or disconnected from a publication,
+ /// or the publication changed the status (specs/18 §6; summary
+ /// {publication, action, status?}).
+ submission;
static ActivityType fromJson(String name) {
switch (name) {
@@ -54,6 +59,8 @@ enum ActivityType implements _isc.SerializableModel {
return ActivityType.exported;
case 'uploaded':
return ActivityType.uploaded;
+ case 'submission':
+ return ActivityType.submission;
default:
throw ArgumentError(
'Value "$name" cannot be converted to "ActivityType"',
diff --git a/wonky_client/lib/src/protocol/platform/comments/comment.dart b/wonky_client/lib/src/protocol/platform/comments/comment.dart
index 0a916692..d4a75c69 100644
--- a/wonky_client/lib/src/protocol/platform/comments/comment.dart
+++ b/wonky_client/lib/src/protocol/platform/comments/comment.dart
@@ -30,6 +30,7 @@ abstract class Comment
this.editedAt,
this.deletedAt,
this.systemEvent,
+ this.publicationId,
}) : reactions = reactions ?? '{}';
factory Comment({
@@ -45,6 +46,7 @@ abstract class Comment
DateTime? editedAt,
DateTime? deletedAt,
_ikzs0dm0.CommentSystemEvent? systemEvent,
+ int? publicationId,
}) = _CommentImpl;
factory Comment.fromJson(Map jsonSerialization) {
@@ -77,6 +79,7 @@ abstract class Comment
: _ikzs0dm0.CommentSystemEvent.fromJson(
(jsonSerialization['systemEvent'] as String),
),
+ publicationId: jsonSerialization['publicationId'] as int?,
);
}
@@ -113,6 +116,9 @@ abstract class Comment
_ikzs0dm0.CommentSystemEvent? systemEvent;
+ /// (M14) A publication's message (authorKind system), specs/18 §5.2.
+ int? publicationId;
+
/// Returns a shallow copy of this [Comment]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@@ -129,6 +135,7 @@ abstract class Comment
DateTime? editedAt = const _isc.$UndefinedDateTime(),
DateTime? deletedAt = const _isc.$UndefinedDateTime(),
_ikzs0dm0.CommentSystemEvent? systemEvent,
+ int? publicationId,
});
@override
Map toJson() {
@@ -146,6 +153,7 @@ abstract class Comment
if (editedAt != null) 'editedAt': editedAt?.toJson(),
if (deletedAt != null) 'deletedAt': deletedAt?.toJson(),
if (systemEvent != null) 'systemEvent': systemEvent?.toJson(),
+ if (publicationId != null) 'publicationId': publicationId,
};
}
@@ -165,6 +173,7 @@ abstract class Comment
if (editedAt != null) 'editedAt': editedAt?.toJson(),
if (deletedAt != null) 'deletedAt': deletedAt?.toJson(),
if (systemEvent != null) 'systemEvent': systemEvent?.toJson(),
+ if (publicationId != null) 'publicationId': publicationId,
};
}
@@ -190,6 +199,7 @@ class _CommentImpl extends Comment {
DateTime? editedAt,
DateTime? deletedAt,
_ikzs0dm0.CommentSystemEvent? systemEvent,
+ int? publicationId,
}) : super._(
id: id,
threadId: threadId,
@@ -203,6 +213,7 @@ class _CommentImpl extends Comment {
editedAt: editedAt,
deletedAt: deletedAt,
systemEvent: systemEvent,
+ publicationId: publicationId,
);
/// Returns a shallow copy of this [Comment]
@@ -222,6 +233,7 @@ class _CommentImpl extends Comment {
DateTime? editedAt = const _isc.$UndefinedDateTime(),
DateTime? deletedAt = const _isc.$UndefinedDateTime(),
Object? systemEvent = _Undefined,
+ Object? publicationId = _Undefined,
}) {
return Comment(
id: id is int? ? id : this.id,
@@ -240,6 +252,7 @@ class _CommentImpl extends Comment {
systemEvent: systemEvent is _ikzs0dm0.CommentSystemEvent?
? systemEvent
: this.systemEvent,
+ publicationId: publicationId is int? ? publicationId : this.publicationId,
);
}
}
diff --git a/wonky_client/lib/src/protocol/platform/comments/comment_view.dart b/wonky_client/lib/src/protocol/platform/comments/comment_view.dart
index ec16e983..209eefb0 100644
--- a/wonky_client/lib/src/protocol/platform/comments/comment_view.dart
+++ b/wonky_client/lib/src/protocol/platform/comments/comment_view.dart
@@ -31,6 +31,7 @@ abstract class CommentView
this.editedAt,
required this.deleted,
this.systemEvent,
+ this.authorLabel,
});
factory CommentView({
@@ -46,6 +47,7 @@ abstract class CommentView
DateTime? editedAt,
required bool deleted,
_ikzs0dm0.CommentSystemEvent? systemEvent,
+ String? authorLabel,
}) = _CommentViewImpl;
factory CommentView.fromJson(Map jsonSerialization) {
@@ -84,6 +86,7 @@ abstract class CommentView
: _ikzs0dm0.CommentSystemEvent.fromJson(
(jsonSerialization['systemEvent'] as String),
),
+ authorLabel: jsonSerialization['authorLabel'] as String?,
);
}
@@ -113,6 +116,10 @@ abstract class CommentView
_ikzs0dm0.CommentSystemEvent? systemEvent;
+ /// (M14) Name shown instead of a user: the publication of a message
+ /// (specs/18 §5.2).
+ String? authorLabel;
+
/// Returns a shallow copy of this [CommentView]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@@ -129,6 +136,7 @@ abstract class CommentView
DateTime? editedAt = const _isc.$UndefinedDateTime(),
bool? deleted,
_ikzs0dm0.CommentSystemEvent? systemEvent,
+ String? authorLabel,
});
@override
Map toJson() {
@@ -146,6 +154,7 @@ abstract class CommentView
if (editedAt != null) 'editedAt': editedAt?.toJson(),
'deleted': deleted,
if (systemEvent != null) 'systemEvent': systemEvent?.toJson(),
+ if (authorLabel != null) 'authorLabel': authorLabel,
};
}
@@ -165,6 +174,7 @@ abstract class CommentView
if (editedAt != null) 'editedAt': editedAt?.toJson(),
'deleted': deleted,
if (systemEvent != null) 'systemEvent': systemEvent?.toJson(),
+ if (authorLabel != null) 'authorLabel': authorLabel,
};
}
@@ -195,6 +205,7 @@ class _CommentViewImpl extends CommentView {
DateTime? editedAt,
required bool deleted,
_ikzs0dm0.CommentSystemEvent? systemEvent,
+ String? authorLabel,
}) : super._(
id: id,
uuid: uuid,
@@ -208,6 +219,7 @@ class _CommentViewImpl extends CommentView {
editedAt: editedAt,
deleted: deleted,
systemEvent: systemEvent,
+ authorLabel: authorLabel,
);
/// Returns a shallow copy of this [CommentView]
@@ -227,6 +239,7 @@ class _CommentViewImpl extends CommentView {
DateTime? editedAt = const _isc.$UndefinedDateTime(),
bool? deleted,
Object? systemEvent = _Undefined,
+ Object? authorLabel = _Undefined,
}) {
return CommentView(
id: id ?? this.id,
@@ -255,6 +268,7 @@ class _CommentViewImpl extends CommentView {
systemEvent: systemEvent is _ikzs0dm0.CommentSystemEvent?
? systemEvent
: this.systemEvent,
+ authorLabel: authorLabel is String? ? authorLabel : this.authorLabel,
);
}
}
diff --git a/wonky_client/lib/src/protocol/platform/notifications/notification_kind.dart b/wonky_client/lib/src/protocol/platform/notifications/notification_kind.dart
index e2b12316..e02642de 100644
--- a/wonky_client/lib/src/protocol/platform/notifications/notification_kind.dart
+++ b/wonky_client/lib/src/protocol/platform/notifications/notification_kind.dart
@@ -35,7 +35,11 @@ enum NotificationKind implements _isc.SerializableModel {
boardStatus,
/// My claim: warning, expiry, released, PR closed.
- boardClaim;
+ boardClaim,
+
+ /// (M14) A publication changed the status of a story I can edit or sent
+ /// a message; a submission could not be delivered (specs/18 §5.3).
+ submission;
static NotificationKind fromJson(String name) {
switch (name) {
@@ -65,6 +69,8 @@ enum NotificationKind implements _isc.SerializableModel {
return NotificationKind.boardStatus;
case 'boardClaim':
return NotificationKind.boardClaim;
+ case 'submission':
+ return NotificationKind.submission;
default:
throw ArgumentError(
'Value "$name" cannot be converted to "NotificationKind"',
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/publication_card.dart b/wonky_client/lib/src/protocol/platform/publications/models/publication_card.dart
new file mode 100644
index 00000000..b4dc234f
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/publication_card.dart
@@ -0,0 +1,246 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+import 'package:wonky_client/src/protocol/protocol.dart' as _i8pkqg1o;
+import '../../../platform/publications/models/publication_fields.dart'
+ as _i1a71r29;
+import '../../../platform/publications/models/publication_open_submission.dart'
+ as _i2kst26h;
+
+/// What a contributor sees of a publication (specs/18 §3, §9.1).
+abstract class PublicationCard
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ PublicationCard._({
+ required this.publicId,
+ required this.name,
+ required this.shownHost,
+ required this.verified,
+ required this.siteHost,
+ required this.webhookHost,
+ this.termsUrl,
+ required this.fields,
+ required this.ownerName,
+ required this.enabled,
+ required this.consentNeeded,
+ required this.blocked,
+ required this.openSubmissions,
+ });
+
+ factory PublicationCard({
+ required String publicId,
+ required String name,
+ required String shownHost,
+ required bool verified,
+ required String siteHost,
+ required String webhookHost,
+ String? termsUrl,
+ required _i1a71r29.PublicationFields fields,
+ required String ownerName,
+ required bool enabled,
+ required bool consentNeeded,
+ required bool blocked,
+ required List<_i2kst26h.PublicationOpenSubmission> openSubmissions,
+ }) = _PublicationCardImpl;
+
+ factory PublicationCard.fromJson(Map jsonSerialization) {
+ return PublicationCard(
+ publicId: jsonSerialization['publicId'] as String,
+ name: jsonSerialization['name'] as String,
+ shownHost: jsonSerialization['shownHost'] as String,
+ verified: _isc.BoolJsonExtension.fromJson(jsonSerialization['verified']),
+ siteHost: jsonSerialization['siteHost'] as String,
+ webhookHost: jsonSerialization['webhookHost'] as String,
+ termsUrl: jsonSerialization['termsUrl'] as String?,
+ fields: _i8pkqg1o.Protocol().deserialize<_i1a71r29.PublicationFields>(
+ jsonSerialization['fields'],
+ ),
+ ownerName: jsonSerialization['ownerName'] as String,
+ enabled: _isc.BoolJsonExtension.fromJson(jsonSerialization['enabled']),
+ consentNeeded: _isc.BoolJsonExtension.fromJson(
+ jsonSerialization['consentNeeded'],
+ ),
+ blocked: _isc.BoolJsonExtension.fromJson(jsonSerialization['blocked']),
+ openSubmissions: _i8pkqg1o.Protocol()
+ .deserialize>(
+ jsonSerialization['openSubmissions'],
+ ),
+ );
+ }
+
+ String publicId;
+
+ String name;
+
+ /// The verified site host, or the webhook host when unverified.
+ String shownHost;
+
+ bool verified;
+
+ String siteHost;
+
+ String webhookHost;
+
+ String? termsUrl;
+
+ _i1a71r29.PublicationFields fields;
+
+ String ownerName;
+
+ bool enabled;
+
+ /// The caller must agree first (never asked or what was agreed changed).
+ bool consentNeeded;
+
+ /// The publication doesn't take stories from the caller.
+ bool blocked;
+
+ /// The caller's drafts and stories with changes requested.
+ List<_i2kst26h.PublicationOpenSubmission> openSubmissions;
+
+ /// Returns a shallow copy of this [PublicationCard]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ PublicationCard copyWith({
+ String? publicId,
+ String? name,
+ String? shownHost,
+ bool? verified,
+ String? siteHost,
+ String? webhookHost,
+ String? termsUrl,
+ _i1a71r29.PublicationFields? fields,
+ String? ownerName,
+ bool? enabled,
+ bool? consentNeeded,
+ bool? blocked,
+ List<_i2kst26h.PublicationOpenSubmission>? openSubmissions,
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'PublicationCard',
+ 'publicId': publicId,
+ 'name': name,
+ 'shownHost': shownHost,
+ 'verified': verified,
+ 'siteHost': siteHost,
+ 'webhookHost': webhookHost,
+ if (termsUrl != null) 'termsUrl': termsUrl,
+ 'fields': fields.toJson(),
+ 'ownerName': ownerName,
+ 'enabled': enabled,
+ 'consentNeeded': consentNeeded,
+ 'blocked': blocked,
+ 'openSubmissions': openSubmissions.toJson(valueToJson: (v) => v.toJson()),
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'PublicationCard',
+ 'publicId': publicId,
+ 'name': name,
+ 'shownHost': shownHost,
+ 'verified': verified,
+ 'siteHost': siteHost,
+ 'webhookHost': webhookHost,
+ if (termsUrl != null) 'termsUrl': termsUrl,
+ 'fields': fields.toJsonForProtocol(),
+ 'ownerName': ownerName,
+ 'enabled': enabled,
+ 'consentNeeded': consentNeeded,
+ 'blocked': blocked,
+ 'openSubmissions': openSubmissions.toJson(
+ valueToJson: (v) => v.toJsonForProtocol(),
+ ),
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _Undefined {}
+
+class _PublicationCardImpl extends PublicationCard {
+ _PublicationCardImpl({
+ required String publicId,
+ required String name,
+ required String shownHost,
+ required bool verified,
+ required String siteHost,
+ required String webhookHost,
+ String? termsUrl,
+ required _i1a71r29.PublicationFields fields,
+ required String ownerName,
+ required bool enabled,
+ required bool consentNeeded,
+ required bool blocked,
+ required List<_i2kst26h.PublicationOpenSubmission> openSubmissions,
+ }) : super._(
+ publicId: publicId,
+ name: name,
+ shownHost: shownHost,
+ verified: verified,
+ siteHost: siteHost,
+ webhookHost: webhookHost,
+ termsUrl: termsUrl,
+ fields: fields,
+ ownerName: ownerName,
+ enabled: enabled,
+ consentNeeded: consentNeeded,
+ blocked: blocked,
+ openSubmissions: openSubmissions,
+ );
+
+ /// Returns a shallow copy of this [PublicationCard]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ PublicationCard copyWith({
+ String? publicId,
+ String? name,
+ String? shownHost,
+ bool? verified,
+ String? siteHost,
+ String? webhookHost,
+ Object? termsUrl = _Undefined,
+ _i1a71r29.PublicationFields? fields,
+ String? ownerName,
+ bool? enabled,
+ bool? consentNeeded,
+ bool? blocked,
+ List<_i2kst26h.PublicationOpenSubmission>? openSubmissions,
+ }) {
+ return PublicationCard(
+ publicId: publicId ?? this.publicId,
+ name: name ?? this.name,
+ shownHost: shownHost ?? this.shownHost,
+ verified: verified ?? this.verified,
+ siteHost: siteHost ?? this.siteHost,
+ webhookHost: webhookHost ?? this.webhookHost,
+ termsUrl: termsUrl is String? ? termsUrl : this.termsUrl,
+ fields: fields ?? this.fields.copyWith(),
+ ownerName: ownerName ?? this.ownerName,
+ enabled: enabled ?? this.enabled,
+ consentNeeded: consentNeeded ?? this.consentNeeded,
+ blocked: blocked ?? this.blocked,
+ openSubmissions:
+ openSubmissions ??
+ this.openSubmissions.map((e0) => e0.copyWith()).toList(),
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/publication_check_result.dart b/wonky_client/lib/src/protocol/platform/publications/models/publication_check_result.dart
new file mode 100644
index 00000000..736e2ae4
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/publication_check_result.dart
@@ -0,0 +1,114 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+import 'package:wonky_client/src/protocol/protocol.dart' as _i8pkqg1o;
+import '../../../platform/publications/models/publication_settings.dart'
+ as _iyzf06w5;
+
+/// Result of *Verify* or *Send test* (specs/18 §8.1, §8.2).
+abstract class PublicationCheckResult
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ PublicationCheckResult._({
+ required this.ok,
+ this.message,
+ required this.settings,
+ });
+
+ factory PublicationCheckResult({
+ required bool ok,
+ String? message,
+ required _iyzf06w5.PublicationSettings settings,
+ }) = _PublicationCheckResultImpl;
+
+ factory PublicationCheckResult.fromJson(
+ Map jsonSerialization,
+ ) {
+ return PublicationCheckResult(
+ ok: _isc.BoolJsonExtension.fromJson(jsonSerialization['ok']),
+ message: jsonSerialization['message'] as String?,
+ settings: _i8pkqg1o.Protocol().deserialize<_iyzf06w5.PublicationSettings>(
+ jsonSerialization['settings'],
+ ),
+ );
+ }
+
+ bool ok;
+
+ /// What went wrong, for the owner.
+ String? message;
+
+ _iyzf06w5.PublicationSettings settings;
+
+ /// Returns a shallow copy of this [PublicationCheckResult]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ PublicationCheckResult copyWith({
+ bool? ok,
+ String? message,
+ _iyzf06w5.PublicationSettings? settings,
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'PublicationCheckResult',
+ 'ok': ok,
+ if (message != null) 'message': message,
+ 'settings': settings.toJson(),
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'PublicationCheckResult',
+ 'ok': ok,
+ if (message != null) 'message': message,
+ 'settings': settings.toJsonForProtocol(),
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _Undefined {}
+
+class _PublicationCheckResultImpl extends PublicationCheckResult {
+ _PublicationCheckResultImpl({
+ required bool ok,
+ String? message,
+ required _iyzf06w5.PublicationSettings settings,
+ }) : super._(
+ ok: ok,
+ message: message,
+ settings: settings,
+ );
+
+ /// Returns a shallow copy of this [PublicationCheckResult]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ PublicationCheckResult copyWith({
+ bool? ok,
+ Object? message = _Undefined,
+ _iyzf06w5.PublicationSettings? settings,
+ }) {
+ return PublicationCheckResult(
+ ok: ok ?? this.ok,
+ message: message is String? ? message : this.message,
+ settings: settings ?? this.settings.copyWith(),
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/publication_delivery_view.dart b/wonky_client/lib/src/protocol/platform/publications/models/publication_delivery_view.dart
new file mode 100644
index 00000000..7c9754b4
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/publication_delivery_view.dart
@@ -0,0 +1,196 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+
+/// A webhook delivery in the owner's *Deliveries* list (specs/18 §8.1).
+abstract class PublicationDeliveryView
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ PublicationDeliveryView._({
+ required this.webhookId,
+ required this.type,
+ required this.createdAt,
+ required this.attempts,
+ this.lastStatus,
+ this.lastError,
+ this.deliveredAt,
+ this.failedAt,
+ this.nextAttemptAt,
+ });
+
+ factory PublicationDeliveryView({
+ required String webhookId,
+ required String type,
+ required DateTime createdAt,
+ required int attempts,
+ int? lastStatus,
+ String? lastError,
+ DateTime? deliveredAt,
+ DateTime? failedAt,
+ DateTime? nextAttemptAt,
+ }) = _PublicationDeliveryViewImpl;
+
+ factory PublicationDeliveryView.fromJson(
+ Map jsonSerialization,
+ ) {
+ return PublicationDeliveryView(
+ webhookId: jsonSerialization['webhookId'] as String,
+ type: jsonSerialization['type'] as String,
+ createdAt: _isc.DateTimeJsonExtension.fromJson(
+ jsonSerialization['createdAt'],
+ ),
+ attempts: jsonSerialization['attempts'] as int,
+ lastStatus: jsonSerialization['lastStatus'] as int?,
+ lastError: jsonSerialization['lastError'] as String?,
+ deliveredAt: jsonSerialization['deliveredAt'] == null
+ ? null
+ : _isc.DateTimeJsonExtension.fromJson(
+ jsonSerialization['deliveredAt'],
+ ),
+ failedAt: jsonSerialization['failedAt'] == null
+ ? null
+ : _isc.DateTimeJsonExtension.fromJson(jsonSerialization['failedAt']),
+ nextAttemptAt: jsonSerialization['nextAttemptAt'] == null
+ ? null
+ : _isc.DateTimeJsonExtension.fromJson(
+ jsonSerialization['nextAttemptAt'],
+ ),
+ );
+ }
+
+ String webhookId;
+
+ String type;
+
+ DateTime createdAt;
+
+ int attempts;
+
+ int? lastStatus;
+
+ String? lastError;
+
+ DateTime? deliveredAt;
+
+ DateTime? failedAt;
+
+ DateTime? nextAttemptAt;
+
+ /// Returns a shallow copy of this [PublicationDeliveryView]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ PublicationDeliveryView copyWith({
+ String? webhookId,
+ String? type,
+ DateTime? createdAt,
+ int? attempts,
+ int? lastStatus,
+ String? lastError,
+ DateTime? deliveredAt = const _isc.$UndefinedDateTime(),
+ DateTime? failedAt = const _isc.$UndefinedDateTime(),
+ DateTime? nextAttemptAt = const _isc.$UndefinedDateTime(),
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'PublicationDeliveryView',
+ 'webhookId': webhookId,
+ 'type': type,
+ 'createdAt': createdAt.toJson(),
+ 'attempts': attempts,
+ if (lastStatus != null) 'lastStatus': lastStatus,
+ if (lastError != null) 'lastError': lastError,
+ if (deliveredAt != null) 'deliveredAt': deliveredAt?.toJson(),
+ if (failedAt != null) 'failedAt': failedAt?.toJson(),
+ if (nextAttemptAt != null) 'nextAttemptAt': nextAttemptAt?.toJson(),
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'PublicationDeliveryView',
+ 'webhookId': webhookId,
+ 'type': type,
+ 'createdAt': createdAt.toJson(),
+ 'attempts': attempts,
+ if (lastStatus != null) 'lastStatus': lastStatus,
+ if (lastError != null) 'lastError': lastError,
+ if (deliveredAt != null) 'deliveredAt': deliveredAt?.toJson(),
+ if (failedAt != null) 'failedAt': failedAt?.toJson(),
+ if (nextAttemptAt != null) 'nextAttemptAt': nextAttemptAt?.toJson(),
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _Undefined {}
+
+class _PublicationDeliveryViewImpl extends PublicationDeliveryView {
+ _PublicationDeliveryViewImpl({
+ required String webhookId,
+ required String type,
+ required DateTime createdAt,
+ required int attempts,
+ int? lastStatus,
+ String? lastError,
+ DateTime? deliveredAt,
+ DateTime? failedAt,
+ DateTime? nextAttemptAt,
+ }) : super._(
+ webhookId: webhookId,
+ type: type,
+ createdAt: createdAt,
+ attempts: attempts,
+ lastStatus: lastStatus,
+ lastError: lastError,
+ deliveredAt: deliveredAt,
+ failedAt: failedAt,
+ nextAttemptAt: nextAttemptAt,
+ );
+
+ /// Returns a shallow copy of this [PublicationDeliveryView]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ PublicationDeliveryView copyWith({
+ String? webhookId,
+ String? type,
+ DateTime? createdAt,
+ int? attempts,
+ Object? lastStatus = _Undefined,
+ Object? lastError = _Undefined,
+ DateTime? deliveredAt = const _isc.$UndefinedDateTime(),
+ DateTime? failedAt = const _isc.$UndefinedDateTime(),
+ DateTime? nextAttemptAt = const _isc.$UndefinedDateTime(),
+ }) {
+ return PublicationDeliveryView(
+ webhookId: webhookId ?? this.webhookId,
+ type: type ?? this.type,
+ createdAt: createdAt ?? this.createdAt,
+ attempts: attempts ?? this.attempts,
+ lastStatus: lastStatus is int? ? lastStatus : this.lastStatus,
+ lastError: lastError is String? ? lastError : this.lastError,
+ deliveredAt: deliveredAt is _isc.UndefinedSentinel
+ ? this.deliveredAt
+ : deliveredAt,
+ failedAt: failedAt is _isc.UndefinedSentinel ? this.failedAt : failedAt,
+ nextAttemptAt: nextAttemptAt is _isc.UndefinedSentinel
+ ? this.nextAttemptAt
+ : nextAttemptAt,
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/publication_field_mode.dart b/wonky_client/lib/src/protocol/platform/publications/models/publication_field_mode.dart
new file mode 100644
index 00000000..f3819526
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/publication_field_mode.dart
@@ -0,0 +1,41 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+
+/// Whether a submission field is shown and required (specs/18 §8.1).
+enum PublicationFieldMode implements _isc.SerializableModel {
+ off,
+ optional,
+ required;
+
+ static PublicationFieldMode fromJson(String name) {
+ switch (name) {
+ case 'off':
+ return PublicationFieldMode.off;
+ case 'optional':
+ return PublicationFieldMode.optional;
+ case 'required':
+ return PublicationFieldMode.required;
+ default:
+ throw ArgumentError(
+ 'Value "$name" cannot be converted to "PublicationFieldMode"',
+ );
+ }
+ }
+
+ @override
+ String toJson() => name;
+
+ @override
+ String toString() => name;
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/publication_fields.dart b/wonky_client/lib/src/protocol/platform/publications/models/publication_fields.dart
new file mode 100644
index 00000000..d009104f
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/publication_fields.dart
@@ -0,0 +1,157 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+import 'package:wonky_client/src/protocol/protocol.dart' as _i8pkqg1o;
+import '../../../platform/publications/models/publication_field_mode.dart'
+ as _i5yv7v4s;
+
+/// Which submission fields a publication asks for (specs/18 §8.1). The
+/// title is always asked for and required.
+abstract class PublicationFields
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ PublicationFields._({
+ required this.summary,
+ required this.tags,
+ required this.allowedTags,
+ required this.coverImage,
+ required this.authorBio,
+ required this.note,
+ });
+
+ factory PublicationFields({
+ required _i5yv7v4s.PublicationFieldMode summary,
+ required _i5yv7v4s.PublicationFieldMode tags,
+ required List allowedTags,
+ required _i5yv7v4s.PublicationFieldMode coverImage,
+ required _i5yv7v4s.PublicationFieldMode authorBio,
+ required _i5yv7v4s.PublicationFieldMode note,
+ }) = _PublicationFieldsImpl;
+
+ factory PublicationFields.fromJson(Map jsonSerialization) {
+ return PublicationFields(
+ summary: _i5yv7v4s.PublicationFieldMode.fromJson(
+ (jsonSerialization['summary'] as String),
+ ),
+ tags: _i5yv7v4s.PublicationFieldMode.fromJson(
+ (jsonSerialization['tags'] as String),
+ ),
+ allowedTags: _i8pkqg1o.Protocol().deserialize>(
+ jsonSerialization['allowedTags'],
+ ),
+ coverImage: _i5yv7v4s.PublicationFieldMode.fromJson(
+ (jsonSerialization['coverImage'] as String),
+ ),
+ authorBio: _i5yv7v4s.PublicationFieldMode.fromJson(
+ (jsonSerialization['authorBio'] as String),
+ ),
+ note: _i5yv7v4s.PublicationFieldMode.fromJson(
+ (jsonSerialization['note'] as String),
+ ),
+ );
+ }
+
+ _i5yv7v4s.PublicationFieldMode summary;
+
+ _i5yv7v4s.PublicationFieldMode tags;
+
+ /// When not empty, tags must come from this list.
+ List allowedTags;
+
+ _i5yv7v4s.PublicationFieldMode coverImage;
+
+ _i5yv7v4s.PublicationFieldMode authorBio;
+
+ _i5yv7v4s.PublicationFieldMode note;
+
+ /// Returns a shallow copy of this [PublicationFields]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ PublicationFields copyWith({
+ _i5yv7v4s.PublicationFieldMode? summary,
+ _i5yv7v4s.PublicationFieldMode? tags,
+ List? allowedTags,
+ _i5yv7v4s.PublicationFieldMode? coverImage,
+ _i5yv7v4s.PublicationFieldMode? authorBio,
+ _i5yv7v4s.PublicationFieldMode? note,
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'PublicationFields',
+ 'summary': summary.toJson(),
+ 'tags': tags.toJson(),
+ 'allowedTags': allowedTags.toJson(),
+ 'coverImage': coverImage.toJson(),
+ 'authorBio': authorBio.toJson(),
+ 'note': note.toJson(),
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'PublicationFields',
+ 'summary': summary.toJson(),
+ 'tags': tags.toJson(),
+ 'allowedTags': allowedTags.toJson(),
+ 'coverImage': coverImage.toJson(),
+ 'authorBio': authorBio.toJson(),
+ 'note': note.toJson(),
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _PublicationFieldsImpl extends PublicationFields {
+ _PublicationFieldsImpl({
+ required _i5yv7v4s.PublicationFieldMode summary,
+ required _i5yv7v4s.PublicationFieldMode tags,
+ required List allowedTags,
+ required _i5yv7v4s.PublicationFieldMode coverImage,
+ required _i5yv7v4s.PublicationFieldMode authorBio,
+ required _i5yv7v4s.PublicationFieldMode note,
+ }) : super._(
+ summary: summary,
+ tags: tags,
+ allowedTags: allowedTags,
+ coverImage: coverImage,
+ authorBio: authorBio,
+ note: note,
+ );
+
+ /// Returns a shallow copy of this [PublicationFields]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ PublicationFields copyWith({
+ _i5yv7v4s.PublicationFieldMode? summary,
+ _i5yv7v4s.PublicationFieldMode? tags,
+ List? allowedTags,
+ _i5yv7v4s.PublicationFieldMode? coverImage,
+ _i5yv7v4s.PublicationFieldMode? authorBio,
+ _i5yv7v4s.PublicationFieldMode? note,
+ }) {
+ return PublicationFields(
+ summary: summary ?? this.summary,
+ tags: tags ?? this.tags,
+ allowedTags: allowedTags ?? this.allowedTags.map((e0) => e0).toList(),
+ coverImage: coverImage ?? this.coverImage,
+ authorBio: authorBio ?? this.authorBio,
+ note: note ?? this.note,
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/publication_input.dart b/wonky_client/lib/src/protocol/platform/publications/models/publication_input.dart
new file mode 100644
index 00000000..c2be5a54
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/publication_input.dart
@@ -0,0 +1,167 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+import 'package:wonky_client/src/protocol/protocol.dart' as _i8pkqg1o;
+import '../../../platform/publications/models/publication_fields.dart'
+ as _i1a71r29;
+
+/// Create or update a publication (specs/18 §8.1).
+abstract class PublicationInput
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ PublicationInput._({
+ required this.name,
+ required this.siteUrl,
+ required this.webhookUrl,
+ this.templateItemUuid,
+ this.termsUrl,
+ required this.fields,
+ required this.enabled,
+ });
+
+ factory PublicationInput({
+ required String name,
+ required String siteUrl,
+ required String webhookUrl,
+ _isc.UuidValue? templateItemUuid,
+ String? termsUrl,
+ required _i1a71r29.PublicationFields fields,
+ required bool enabled,
+ }) = _PublicationInputImpl;
+
+ factory PublicationInput.fromJson(Map jsonSerialization) {
+ return PublicationInput(
+ name: jsonSerialization['name'] as String,
+ siteUrl: jsonSerialization['siteUrl'] as String,
+ webhookUrl: jsonSerialization['webhookUrl'] as String,
+ templateItemUuid: jsonSerialization['templateItemUuid'] == null
+ ? null
+ : _isc.UuidValueJsonExtension.fromJson(
+ jsonSerialization['templateItemUuid'],
+ ),
+ termsUrl: jsonSerialization['termsUrl'] as String?,
+ fields: _i8pkqg1o.Protocol().deserialize<_i1a71r29.PublicationFields>(
+ jsonSerialization['fields'],
+ ),
+ enabled: _isc.BoolJsonExtension.fromJson(jsonSerialization['enabled']),
+ );
+ }
+
+ String name;
+
+ String siteUrl;
+
+ String webhookUrl;
+
+ _isc.UuidValue? templateItemUuid;
+
+ String? termsUrl;
+
+ _i1a71r29.PublicationFields fields;
+
+ bool enabled;
+
+ /// Returns a shallow copy of this [PublicationInput]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ PublicationInput copyWith({
+ String? name,
+ String? siteUrl,
+ String? webhookUrl,
+ _isc.UuidValue? templateItemUuid = const _isc.$UndefinedUuidValue(),
+ String? termsUrl,
+ _i1a71r29.PublicationFields? fields,
+ bool? enabled,
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'PublicationInput',
+ 'name': name,
+ 'siteUrl': siteUrl,
+ 'webhookUrl': webhookUrl,
+ if (templateItemUuid != null)
+ 'templateItemUuid': templateItemUuid?.toJson(),
+ if (termsUrl != null) 'termsUrl': termsUrl,
+ 'fields': fields.toJson(),
+ 'enabled': enabled,
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'PublicationInput',
+ 'name': name,
+ 'siteUrl': siteUrl,
+ 'webhookUrl': webhookUrl,
+ if (templateItemUuid != null)
+ 'templateItemUuid': templateItemUuid?.toJson(),
+ if (termsUrl != null) 'termsUrl': termsUrl,
+ 'fields': fields.toJsonForProtocol(),
+ 'enabled': enabled,
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _Undefined {}
+
+class _PublicationInputImpl extends PublicationInput {
+ _PublicationInputImpl({
+ required String name,
+ required String siteUrl,
+ required String webhookUrl,
+ _isc.UuidValue? templateItemUuid,
+ String? termsUrl,
+ required _i1a71r29.PublicationFields fields,
+ required bool enabled,
+ }) : super._(
+ name: name,
+ siteUrl: siteUrl,
+ webhookUrl: webhookUrl,
+ templateItemUuid: templateItemUuid,
+ termsUrl: termsUrl,
+ fields: fields,
+ enabled: enabled,
+ );
+
+ /// Returns a shallow copy of this [PublicationInput]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ PublicationInput copyWith({
+ String? name,
+ String? siteUrl,
+ String? webhookUrl,
+ _isc.UuidValue? templateItemUuid = const _isc.$UndefinedUuidValue(),
+ Object? termsUrl = _Undefined,
+ _i1a71r29.PublicationFields? fields,
+ bool? enabled,
+ }) {
+ return PublicationInput(
+ name: name ?? this.name,
+ siteUrl: siteUrl ?? this.siteUrl,
+ webhookUrl: webhookUrl ?? this.webhookUrl,
+ templateItemUuid: templateItemUuid is _isc.UndefinedSentinel
+ ? this.templateItemUuid
+ : templateItemUuid,
+ termsUrl: termsUrl is String? ? termsUrl : this.termsUrl,
+ fields: fields ?? this.fields.copyWith(),
+ enabled: enabled ?? this.enabled,
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/publication_moderator_view.dart b/wonky_client/lib/src/protocol/platform/publications/models/publication_moderator_view.dart
new file mode 100644
index 00000000..dc6d852c
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/publication_moderator_view.dart
@@ -0,0 +1,96 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+
+abstract class PublicationModeratorView
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ PublicationModeratorView._({
+ required this.email,
+ this.name,
+ });
+
+ factory PublicationModeratorView({
+ required String email,
+ String? name,
+ }) = _PublicationModeratorViewImpl;
+
+ factory PublicationModeratorView.fromJson(
+ Map jsonSerialization,
+ ) {
+ return PublicationModeratorView(
+ email: jsonSerialization['email'] as String,
+ name: jsonSerialization['name'] as String?,
+ );
+ }
+
+ String email;
+
+ /// Null while no account has this email.
+ String? name;
+
+ /// Returns a shallow copy of this [PublicationModeratorView]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ PublicationModeratorView copyWith({
+ String? email,
+ String? name,
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'PublicationModeratorView',
+ 'email': email,
+ if (name != null) 'name': name,
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'PublicationModeratorView',
+ 'email': email,
+ if (name != null) 'name': name,
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _Undefined {}
+
+class _PublicationModeratorViewImpl extends PublicationModeratorView {
+ _PublicationModeratorViewImpl({
+ required String email,
+ String? name,
+ }) : super._(
+ email: email,
+ name: name,
+ );
+
+ /// Returns a shallow copy of this [PublicationModeratorView]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ PublicationModeratorView copyWith({
+ String? email,
+ Object? name = _Undefined,
+ }) {
+ return PublicationModeratorView(
+ email: email ?? this.email,
+ name: name is String? ? name : this.name,
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/publication_open_submission.dart b/wonky_client/lib/src/protocol/platform/publications/models/publication_open_submission.dart
new file mode 100644
index 00000000..b59c30e2
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/publication_open_submission.dart
@@ -0,0 +1,126 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+import '../../../platform/publications/models/submission_status.dart'
+ as _igclxad1;
+
+/// A story the caller can continue (specs/18 §3).
+abstract class PublicationOpenSubmission
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ PublicationOpenSubmission._({
+ required this.itemUuid,
+ required this.name,
+ required this.status,
+ required this.updatedAt,
+ });
+
+ factory PublicationOpenSubmission({
+ required _isc.UuidValue itemUuid,
+ required String name,
+ required _igclxad1.SubmissionStatus status,
+ required DateTime updatedAt,
+ }) = _PublicationOpenSubmissionImpl;
+
+ factory PublicationOpenSubmission.fromJson(
+ Map jsonSerialization,
+ ) {
+ return PublicationOpenSubmission(
+ itemUuid: _isc.UuidValueJsonExtension.fromJson(
+ jsonSerialization['itemUuid'],
+ ),
+ name: jsonSerialization['name'] as String,
+ status: _igclxad1.SubmissionStatus.fromJson(
+ (jsonSerialization['status'] as String),
+ ),
+ updatedAt: _isc.DateTimeJsonExtension.fromJson(
+ jsonSerialization['updatedAt'],
+ ),
+ );
+ }
+
+ _isc.UuidValue itemUuid;
+
+ String name;
+
+ _igclxad1.SubmissionStatus status;
+
+ DateTime updatedAt;
+
+ /// Returns a shallow copy of this [PublicationOpenSubmission]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ PublicationOpenSubmission copyWith({
+ _isc.UuidValue? itemUuid,
+ String? name,
+ _igclxad1.SubmissionStatus? status,
+ DateTime? updatedAt,
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'PublicationOpenSubmission',
+ 'itemUuid': itemUuid.toJson(),
+ 'name': name,
+ 'status': status.toJson(),
+ 'updatedAt': updatedAt.toJson(),
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'PublicationOpenSubmission',
+ 'itemUuid': itemUuid.toJson(),
+ 'name': name,
+ 'status': status.toJson(),
+ 'updatedAt': updatedAt.toJson(),
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _PublicationOpenSubmissionImpl extends PublicationOpenSubmission {
+ _PublicationOpenSubmissionImpl({
+ required _isc.UuidValue itemUuid,
+ required String name,
+ required _igclxad1.SubmissionStatus status,
+ required DateTime updatedAt,
+ }) : super._(
+ itemUuid: itemUuid,
+ name: name,
+ status: status,
+ updatedAt: updatedAt,
+ );
+
+ /// Returns a shallow copy of this [PublicationOpenSubmission]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ PublicationOpenSubmission copyWith({
+ _isc.UuidValue? itemUuid,
+ String? name,
+ _igclxad1.SubmissionStatus? status,
+ DateTime? updatedAt,
+ }) {
+ return PublicationOpenSubmission(
+ itemUuid: itemUuid ?? this.itemUuid,
+ name: name ?? this.name,
+ status: status ?? this.status,
+ updatedAt: updatedAt ?? this.updatedAt,
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/publication_secret.dart b/wonky_client/lib/src/protocol/platform/publications/models/publication_secret.dart
new file mode 100644
index 00000000..854a6f46
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/publication_secret.dart
@@ -0,0 +1,98 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+import 'package:wonky_client/src/protocol/protocol.dart' as _i8pkqg1o;
+import '../../../platform/publications/models/publication_settings.dart'
+ as _iyzf06w5;
+
+/// A secret shown once (specs/18 §8.1): the signing secret at creation or
+/// rotation, or an API key.
+abstract class PublicationSecret
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ PublicationSecret._({
+ required this.settings,
+ required this.secret,
+ });
+
+ factory PublicationSecret({
+ required _iyzf06w5.PublicationSettings settings,
+ required String secret,
+ }) = _PublicationSecretImpl;
+
+ factory PublicationSecret.fromJson(Map jsonSerialization) {
+ return PublicationSecret(
+ settings: _i8pkqg1o.Protocol().deserialize<_iyzf06w5.PublicationSettings>(
+ jsonSerialization['settings'],
+ ),
+ secret: jsonSerialization['secret'] as String,
+ );
+ }
+
+ _iyzf06w5.PublicationSettings settings;
+
+ String secret;
+
+ /// Returns a shallow copy of this [PublicationSecret]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ PublicationSecret copyWith({
+ _iyzf06w5.PublicationSettings? settings,
+ String? secret,
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'PublicationSecret',
+ 'settings': settings.toJson(),
+ 'secret': secret,
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'PublicationSecret',
+ 'settings': settings.toJsonForProtocol(),
+ 'secret': secret,
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _PublicationSecretImpl extends PublicationSecret {
+ _PublicationSecretImpl({
+ required _iyzf06w5.PublicationSettings settings,
+ required String secret,
+ }) : super._(
+ settings: settings,
+ secret: secret,
+ );
+
+ /// Returns a shallow copy of this [PublicationSecret]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ PublicationSecret copyWith({
+ _iyzf06w5.PublicationSettings? settings,
+ String? secret,
+ }) {
+ return PublicationSecret(
+ settings: settings ?? this.settings.copyWith(),
+ secret: secret ?? this.secret,
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/publication_settings.dart b/wonky_client/lib/src/protocol/platform/publications/models/publication_settings.dart
new file mode 100644
index 00000000..c21f893a
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/publication_settings.dart
@@ -0,0 +1,328 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+import 'package:wonky_client/src/protocol/protocol.dart' as _i8pkqg1o;
+import '../../../platform/publications/models/publication_fields.dart'
+ as _i1a71r29;
+import '../../../platform/publications/models/publication_moderator_view.dart'
+ as _in8lpp1q;
+
+/// The owner's view of a publication (specs/18 §8.1). Never holds secrets.
+abstract class PublicationSettings
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ PublicationSettings._({
+ required this.publicId,
+ required this.name,
+ required this.siteUrl,
+ required this.webhookUrl,
+ this.verifiedHost,
+ this.verifiedAt,
+ this.templateItemUuid,
+ this.templateName,
+ this.termsUrl,
+ required this.fields,
+ required this.moderators,
+ required this.hasApiKey,
+ this.apiKeyLast4,
+ this.previousSecretUntil,
+ required this.enabled,
+ required this.contributorLink,
+ required this.openSubmissionCount,
+ required this.createdAt,
+ });
+
+ factory PublicationSettings({
+ required String publicId,
+ required String name,
+ required String siteUrl,
+ required String webhookUrl,
+ String? verifiedHost,
+ DateTime? verifiedAt,
+ _isc.UuidValue? templateItemUuid,
+ String? templateName,
+ String? termsUrl,
+ required _i1a71r29.PublicationFields fields,
+ required List<_in8lpp1q.PublicationModeratorView> moderators,
+ required bool hasApiKey,
+ String? apiKeyLast4,
+ DateTime? previousSecretUntil,
+ required bool enabled,
+ required String contributorLink,
+ required int openSubmissionCount,
+ required DateTime createdAt,
+ }) = _PublicationSettingsImpl;
+
+ factory PublicationSettings.fromJson(Map jsonSerialization) {
+ return PublicationSettings(
+ publicId: jsonSerialization['publicId'] as String,
+ name: jsonSerialization['name'] as String,
+ siteUrl: jsonSerialization['siteUrl'] as String,
+ webhookUrl: jsonSerialization['webhookUrl'] as String,
+ verifiedHost: jsonSerialization['verifiedHost'] as String?,
+ verifiedAt: jsonSerialization['verifiedAt'] == null
+ ? null
+ : _isc.DateTimeJsonExtension.fromJson(
+ jsonSerialization['verifiedAt'],
+ ),
+ templateItemUuid: jsonSerialization['templateItemUuid'] == null
+ ? null
+ : _isc.UuidValueJsonExtension.fromJson(
+ jsonSerialization['templateItemUuid'],
+ ),
+ templateName: jsonSerialization['templateName'] as String?,
+ termsUrl: jsonSerialization['termsUrl'] as String?,
+ fields: _i8pkqg1o.Protocol().deserialize<_i1a71r29.PublicationFields>(
+ jsonSerialization['fields'],
+ ),
+ moderators: _i8pkqg1o.Protocol()
+ .deserialize>(
+ jsonSerialization['moderators'],
+ ),
+ hasApiKey: _isc.BoolJsonExtension.fromJson(
+ jsonSerialization['hasApiKey'],
+ ),
+ apiKeyLast4: jsonSerialization['apiKeyLast4'] as String?,
+ previousSecretUntil: jsonSerialization['previousSecretUntil'] == null
+ ? null
+ : _isc.DateTimeJsonExtension.fromJson(
+ jsonSerialization['previousSecretUntil'],
+ ),
+ enabled: _isc.BoolJsonExtension.fromJson(jsonSerialization['enabled']),
+ contributorLink: jsonSerialization['contributorLink'] as String,
+ openSubmissionCount: jsonSerialization['openSubmissionCount'] as int,
+ createdAt: _isc.DateTimeJsonExtension.fromJson(
+ jsonSerialization['createdAt'],
+ ),
+ );
+ }
+
+ String publicId;
+
+ String name;
+
+ String siteUrl;
+
+ String webhookUrl;
+
+ String? verifiedHost;
+
+ DateTime? verifiedAt;
+
+ _isc.UuidValue? templateItemUuid;
+
+ String? templateName;
+
+ String? termsUrl;
+
+ _i1a71r29.PublicationFields fields;
+
+ List<_in8lpp1q.PublicationModeratorView> moderators;
+
+ bool hasApiKey;
+
+ String? apiKeyLast4;
+
+ /// Set while the previous signing secret still signs.
+ DateTime? previousSecretUntil;
+
+ bool enabled;
+
+ /// `https:///write/new?publication=`.
+ String contributorLink;
+
+ /// Submissions in review or with changes requested.
+ int openSubmissionCount;
+
+ DateTime createdAt;
+
+ /// Returns a shallow copy of this [PublicationSettings]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ PublicationSettings copyWith({
+ String? publicId,
+ String? name,
+ String? siteUrl,
+ String? webhookUrl,
+ String? verifiedHost,
+ DateTime? verifiedAt = const _isc.$UndefinedDateTime(),
+ _isc.UuidValue? templateItemUuid = const _isc.$UndefinedUuidValue(),
+ String? templateName,
+ String? termsUrl,
+ _i1a71r29.PublicationFields? fields,
+ List<_in8lpp1q.PublicationModeratorView>? moderators,
+ bool? hasApiKey,
+ String? apiKeyLast4,
+ DateTime? previousSecretUntil = const _isc.$UndefinedDateTime(),
+ bool? enabled,
+ String? contributorLink,
+ int? openSubmissionCount,
+ DateTime? createdAt,
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'PublicationSettings',
+ 'publicId': publicId,
+ 'name': name,
+ 'siteUrl': siteUrl,
+ 'webhookUrl': webhookUrl,
+ if (verifiedHost != null) 'verifiedHost': verifiedHost,
+ if (verifiedAt != null) 'verifiedAt': verifiedAt?.toJson(),
+ if (templateItemUuid != null)
+ 'templateItemUuid': templateItemUuid?.toJson(),
+ if (templateName != null) 'templateName': templateName,
+ if (termsUrl != null) 'termsUrl': termsUrl,
+ 'fields': fields.toJson(),
+ 'moderators': moderators.toJson(valueToJson: (v) => v.toJson()),
+ 'hasApiKey': hasApiKey,
+ if (apiKeyLast4 != null) 'apiKeyLast4': apiKeyLast4,
+ if (previousSecretUntil != null)
+ 'previousSecretUntil': previousSecretUntil?.toJson(),
+ 'enabled': enabled,
+ 'contributorLink': contributorLink,
+ 'openSubmissionCount': openSubmissionCount,
+ 'createdAt': createdAt.toJson(),
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'PublicationSettings',
+ 'publicId': publicId,
+ 'name': name,
+ 'siteUrl': siteUrl,
+ 'webhookUrl': webhookUrl,
+ if (verifiedHost != null) 'verifiedHost': verifiedHost,
+ if (verifiedAt != null) 'verifiedAt': verifiedAt?.toJson(),
+ if (templateItemUuid != null)
+ 'templateItemUuid': templateItemUuid?.toJson(),
+ if (templateName != null) 'templateName': templateName,
+ if (termsUrl != null) 'termsUrl': termsUrl,
+ 'fields': fields.toJsonForProtocol(),
+ 'moderators': moderators.toJson(
+ valueToJson: (v) => v.toJsonForProtocol(),
+ ),
+ 'hasApiKey': hasApiKey,
+ if (apiKeyLast4 != null) 'apiKeyLast4': apiKeyLast4,
+ if (previousSecretUntil != null)
+ 'previousSecretUntil': previousSecretUntil?.toJson(),
+ 'enabled': enabled,
+ 'contributorLink': contributorLink,
+ 'openSubmissionCount': openSubmissionCount,
+ 'createdAt': createdAt.toJson(),
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _Undefined {}
+
+class _PublicationSettingsImpl extends PublicationSettings {
+ _PublicationSettingsImpl({
+ required String publicId,
+ required String name,
+ required String siteUrl,
+ required String webhookUrl,
+ String? verifiedHost,
+ DateTime? verifiedAt,
+ _isc.UuidValue? templateItemUuid,
+ String? templateName,
+ String? termsUrl,
+ required _i1a71r29.PublicationFields fields,
+ required List<_in8lpp1q.PublicationModeratorView> moderators,
+ required bool hasApiKey,
+ String? apiKeyLast4,
+ DateTime? previousSecretUntil,
+ required bool enabled,
+ required String contributorLink,
+ required int openSubmissionCount,
+ required DateTime createdAt,
+ }) : super._(
+ publicId: publicId,
+ name: name,
+ siteUrl: siteUrl,
+ webhookUrl: webhookUrl,
+ verifiedHost: verifiedHost,
+ verifiedAt: verifiedAt,
+ templateItemUuid: templateItemUuid,
+ templateName: templateName,
+ termsUrl: termsUrl,
+ fields: fields,
+ moderators: moderators,
+ hasApiKey: hasApiKey,
+ apiKeyLast4: apiKeyLast4,
+ previousSecretUntil: previousSecretUntil,
+ enabled: enabled,
+ contributorLink: contributorLink,
+ openSubmissionCount: openSubmissionCount,
+ createdAt: createdAt,
+ );
+
+ /// Returns a shallow copy of this [PublicationSettings]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ PublicationSettings copyWith({
+ String? publicId,
+ String? name,
+ String? siteUrl,
+ String? webhookUrl,
+ Object? verifiedHost = _Undefined,
+ DateTime? verifiedAt = const _isc.$UndefinedDateTime(),
+ _isc.UuidValue? templateItemUuid = const _isc.$UndefinedUuidValue(),
+ Object? templateName = _Undefined,
+ Object? termsUrl = _Undefined,
+ _i1a71r29.PublicationFields? fields,
+ List<_in8lpp1q.PublicationModeratorView>? moderators,
+ bool? hasApiKey,
+ Object? apiKeyLast4 = _Undefined,
+ DateTime? previousSecretUntil = const _isc.$UndefinedDateTime(),
+ bool? enabled,
+ String? contributorLink,
+ int? openSubmissionCount,
+ DateTime? createdAt,
+ }) {
+ return PublicationSettings(
+ publicId: publicId ?? this.publicId,
+ name: name ?? this.name,
+ siteUrl: siteUrl ?? this.siteUrl,
+ webhookUrl: webhookUrl ?? this.webhookUrl,
+ verifiedHost: verifiedHost is String? ? verifiedHost : this.verifiedHost,
+ verifiedAt: verifiedAt is _isc.UndefinedSentinel
+ ? this.verifiedAt
+ : verifiedAt,
+ templateItemUuid: templateItemUuid is _isc.UndefinedSentinel
+ ? this.templateItemUuid
+ : templateItemUuid,
+ templateName: templateName is String? ? templateName : this.templateName,
+ termsUrl: termsUrl is String? ? termsUrl : this.termsUrl,
+ fields: fields ?? this.fields.copyWith(),
+ moderators:
+ moderators ?? this.moderators.map((e0) => e0.copyWith()).toList(),
+ hasApiKey: hasApiKey ?? this.hasApiKey,
+ apiKeyLast4: apiKeyLast4 is String? ? apiKeyLast4 : this.apiKeyLast4,
+ previousSecretUntil: previousSecretUntil is _isc.UndefinedSentinel
+ ? this.previousSecretUntil
+ : previousSecretUntil,
+ enabled: enabled ?? this.enabled,
+ contributorLink: contributorLink ?? this.contributorLink,
+ openSubmissionCount: openSubmissionCount ?? this.openSubmissionCount,
+ createdAt: createdAt ?? this.createdAt,
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/submission_delivery_state.dart b/wonky_client/lib/src/protocol/platform/publications/models/submission_delivery_state.dart
new file mode 100644
index 00000000..94dabbd3
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/submission_delivery_state.dart
@@ -0,0 +1,41 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+
+/// State of the last submit's webhook delivery (specs/18 §7.2).
+enum SubmissionDeliveryState implements _isc.SerializableModel {
+ /// Not accepted yet; the outbox retries.
+ pending,
+
+ /// Every retry failed.
+ failed;
+
+ static SubmissionDeliveryState fromJson(String name) {
+ switch (name) {
+ case 'pending':
+ return SubmissionDeliveryState.pending;
+ case 'failed':
+ return SubmissionDeliveryState.failed;
+ default:
+ throw ArgumentError(
+ 'Value "$name" cannot be converted to "SubmissionDeliveryState"',
+ );
+ }
+ }
+
+ @override
+ String toJson() => name;
+
+ @override
+ String toString() => name;
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/submission_revision_view.dart b/wonky_client/lib/src/protocol/platform/publications/models/submission_revision_view.dart
new file mode 100644
index 00000000..77e64363
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/submission_revision_view.dart
@@ -0,0 +1,134 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+
+abstract class SubmissionRevisionView
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ SubmissionRevisionView._({
+ required this.sequence,
+ required this.revision,
+ required this.submittedByName,
+ required this.submittedAt,
+ this.versionId,
+ });
+
+ factory SubmissionRevisionView({
+ required int sequence,
+ required int revision,
+ required String submittedByName,
+ required DateTime submittedAt,
+ int? versionId,
+ }) = _SubmissionRevisionViewImpl;
+
+ factory SubmissionRevisionView.fromJson(
+ Map jsonSerialization,
+ ) {
+ return SubmissionRevisionView(
+ sequence: jsonSerialization['sequence'] as int,
+ revision: jsonSerialization['revision'] as int,
+ submittedByName: jsonSerialization['submittedByName'] as String,
+ submittedAt: _isc.DateTimeJsonExtension.fromJson(
+ jsonSerialization['submittedAt'],
+ ),
+ versionId: jsonSerialization['versionId'] as int?,
+ );
+ }
+
+ int sequence;
+
+ int revision;
+
+ String submittedByName;
+
+ DateTime submittedAt;
+
+ /// The named version of this submit, when it still exists.
+ int? versionId;
+
+ /// Returns a shallow copy of this [SubmissionRevisionView]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ SubmissionRevisionView copyWith({
+ int? sequence,
+ int? revision,
+ String? submittedByName,
+ DateTime? submittedAt,
+ int? versionId,
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'SubmissionRevisionView',
+ 'sequence': sequence,
+ 'revision': revision,
+ 'submittedByName': submittedByName,
+ 'submittedAt': submittedAt.toJson(),
+ if (versionId != null) 'versionId': versionId,
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'SubmissionRevisionView',
+ 'sequence': sequence,
+ 'revision': revision,
+ 'submittedByName': submittedByName,
+ 'submittedAt': submittedAt.toJson(),
+ if (versionId != null) 'versionId': versionId,
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _Undefined {}
+
+class _SubmissionRevisionViewImpl extends SubmissionRevisionView {
+ _SubmissionRevisionViewImpl({
+ required int sequence,
+ required int revision,
+ required String submittedByName,
+ required DateTime submittedAt,
+ int? versionId,
+ }) : super._(
+ sequence: sequence,
+ revision: revision,
+ submittedByName: submittedByName,
+ submittedAt: submittedAt,
+ versionId: versionId,
+ );
+
+ /// Returns a shallow copy of this [SubmissionRevisionView]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ SubmissionRevisionView copyWith({
+ int? sequence,
+ int? revision,
+ String? submittedByName,
+ DateTime? submittedAt,
+ Object? versionId = _Undefined,
+ }) {
+ return SubmissionRevisionView(
+ sequence: sequence ?? this.sequence,
+ revision: revision ?? this.revision,
+ submittedByName: submittedByName ?? this.submittedByName,
+ submittedAt: submittedAt ?? this.submittedAt,
+ versionId: versionId is int? ? versionId : this.versionId,
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/submission_started.dart b/wonky_client/lib/src/protocol/platform/publications/models/submission_started.dart
new file mode 100644
index 00000000..ad654d81
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/submission_started.dart
@@ -0,0 +1,70 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+
+/// Result of starting a story (specs/18 §3 step 3).
+abstract class SubmissionStarted
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ SubmissionStarted._({required this.itemUuid});
+
+ factory SubmissionStarted({required _isc.UuidValue itemUuid}) =
+ _SubmissionStartedImpl;
+
+ factory SubmissionStarted.fromJson(Map jsonSerialization) {
+ return SubmissionStarted(
+ itemUuid: _isc.UuidValueJsonExtension.fromJson(
+ jsonSerialization['itemUuid'],
+ ),
+ );
+ }
+
+ _isc.UuidValue itemUuid;
+
+ /// Returns a shallow copy of this [SubmissionStarted]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ SubmissionStarted copyWith({_isc.UuidValue? itemUuid});
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'SubmissionStarted',
+ 'itemUuid': itemUuid.toJson(),
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'SubmissionStarted',
+ 'itemUuid': itemUuid.toJson(),
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _SubmissionStartedImpl extends SubmissionStarted {
+ _SubmissionStartedImpl({required _isc.UuidValue itemUuid})
+ : super._(itemUuid: itemUuid);
+
+ /// Returns a shallow copy of this [SubmissionStarted]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ SubmissionStarted copyWith({_isc.UuidValue? itemUuid}) {
+ return SubmissionStarted(itemUuid: itemUuid ?? this.itemUuid);
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/submission_status.dart b/wonky_client/lib/src/protocol/platform/publications/models/submission_status.dart
new file mode 100644
index 00000000..029857a5
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/submission_status.dart
@@ -0,0 +1,53 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+
+/// Lifecycle of a submission (specs/18 §2.1).
+enum SubmissionStatus implements _isc.SerializableModel {
+ /// Started from the link, never submitted.
+ draft,
+ inReview,
+ changesRequested,
+
+ /// Final for this submission.
+ declined,
+ published,
+ withdrawn;
+
+ static SubmissionStatus fromJson(String name) {
+ switch (name) {
+ case 'draft':
+ return SubmissionStatus.draft;
+ case 'inReview':
+ return SubmissionStatus.inReview;
+ case 'changesRequested':
+ return SubmissionStatus.changesRequested;
+ case 'declined':
+ return SubmissionStatus.declined;
+ case 'published':
+ return SubmissionStatus.published;
+ case 'withdrawn':
+ return SubmissionStatus.withdrawn;
+ default:
+ throw ArgumentError(
+ 'Value "$name" cannot be converted to "SubmissionStatus"',
+ );
+ }
+ }
+
+ @override
+ String toJson() => name;
+
+ @override
+ String toString() => name;
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/submission_values.dart b/wonky_client/lib/src/protocol/platform/publications/models/submission_values.dart
new file mode 100644
index 00000000..b7458b5f
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/submission_values.dart
@@ -0,0 +1,152 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+import 'package:wonky_client/src/protocol/protocol.dart' as _i8pkqg1o;
+
+/// The fields a contributor filled in (specs/18 §4.2).
+abstract class SubmissionValues
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ SubmissionValues._({
+ required this.title,
+ this.summary,
+ required this.tags,
+ this.coverImage,
+ this.authorBio,
+ this.note,
+ });
+
+ factory SubmissionValues({
+ required String title,
+ String? summary,
+ required List tags,
+ _isc.UuidValue? coverImage,
+ String? authorBio,
+ String? note,
+ }) = _SubmissionValuesImpl;
+
+ factory SubmissionValues.fromJson(Map jsonSerialization) {
+ return SubmissionValues(
+ title: jsonSerialization['title'] as String,
+ summary: jsonSerialization['summary'] as String?,
+ tags: _i8pkqg1o.Protocol().deserialize>(
+ jsonSerialization['tags'],
+ ),
+ coverImage: jsonSerialization['coverImage'] == null
+ ? null
+ : _isc.UuidValueJsonExtension.fromJson(
+ jsonSerialization['coverImage'],
+ ),
+ authorBio: jsonSerialization['authorBio'] as String?,
+ note: jsonSerialization['note'] as String?,
+ );
+ }
+
+ String title;
+
+ String? summary;
+
+ List tags;
+
+ /// A `wonky-file` uuid of an image in the document.
+ _isc.UuidValue? coverImage;
+
+ String? authorBio;
+
+ String? note;
+
+ /// Returns a shallow copy of this [SubmissionValues]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ SubmissionValues copyWith({
+ String? title,
+ String? summary,
+ List? tags,
+ _isc.UuidValue? coverImage = const _isc.$UndefinedUuidValue(),
+ String? authorBio,
+ String? note,
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'SubmissionValues',
+ 'title': title,
+ if (summary != null) 'summary': summary,
+ 'tags': tags.toJson(),
+ if (coverImage != null) 'coverImage': coverImage?.toJson(),
+ if (authorBio != null) 'authorBio': authorBio,
+ if (note != null) 'note': note,
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'SubmissionValues',
+ 'title': title,
+ if (summary != null) 'summary': summary,
+ 'tags': tags.toJson(),
+ if (coverImage != null) 'coverImage': coverImage?.toJson(),
+ if (authorBio != null) 'authorBio': authorBio,
+ if (note != null) 'note': note,
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _Undefined {}
+
+class _SubmissionValuesImpl extends SubmissionValues {
+ _SubmissionValuesImpl({
+ required String title,
+ String? summary,
+ required List tags,
+ _isc.UuidValue? coverImage,
+ String? authorBio,
+ String? note,
+ }) : super._(
+ title: title,
+ summary: summary,
+ tags: tags,
+ coverImage: coverImage,
+ authorBio: authorBio,
+ note: note,
+ );
+
+ /// Returns a shallow copy of this [SubmissionValues]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ SubmissionValues copyWith({
+ String? title,
+ Object? summary = _Undefined,
+ List? tags,
+ _isc.UuidValue? coverImage = const _isc.$UndefinedUuidValue(),
+ Object? authorBio = _Undefined,
+ Object? note = _Undefined,
+ }) {
+ return SubmissionValues(
+ title: title ?? this.title,
+ summary: summary is String? ? summary : this.summary,
+ tags: tags ?? this.tags.map((e0) => e0).toList(),
+ coverImage: coverImage is _isc.UndefinedSentinel
+ ? this.coverImage
+ : coverImage,
+ authorBio: authorBio is String? ? authorBio : this.authorBio,
+ note: note is String? ? note : this.note,
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/publications/models/submission_view.dart b/wonky_client/lib/src/protocol/platform/publications/models/submission_view.dart
new file mode 100644
index 00000000..3249e395
--- /dev/null
+++ b/wonky_client/lib/src/protocol/platform/publications/models/submission_view.dart
@@ -0,0 +1,368 @@
+/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
+/* To generate run: "serverpod generate" */
+
+// ignore_for_file: implementation_imports
+// ignore_for_file: library_private_types_in_public_api
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: type_literal_in_constant_pattern
+// ignore_for_file: use_super_parameters
+// ignore_for_file: invalid_use_of_internal_member
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:serverpod_client/serverpod_client.dart' as _isc;
+import 'package:wonky_client/src/protocol/protocol.dart' as _i8pkqg1o;
+import '../../../platform/publications/models/publication_fields.dart'
+ as _i1a71r29;
+import '../../../platform/publications/models/submission_delivery_state.dart'
+ as _ifp7j30v;
+import '../../../platform/publications/models/submission_revision_view.dart'
+ as _idtrp1yl;
+import '../../../platform/publications/models/submission_status.dart'
+ as _igclxad1;
+import '../../../platform/publications/models/submission_values.dart'
+ as _igthguer;
+
+/// A document's submission as editors and moderators see it (specs/18 §4).
+abstract class SubmissionView
+ implements _isc.SerializableModel, _isc.ProtocolSerialization {
+ SubmissionView._({
+ required this.id,
+ required this.itemUuid,
+ required this.status,
+ this.delivery,
+ required this.publicationId,
+ required this.publicationName,
+ required this.shownHost,
+ required this.verified,
+ required this.accepting,
+ required this.fields,
+ this.termsUrl,
+ required this.consentNeeded,
+ required this.values,
+ this.publicUrl,
+ this.feedbackThreadUuid,
+ this.lastMessage,
+ this.lastMessageAt,
+ this.lastSubmittedRevision,
+ required this.revisions,
+ required this.updatedAt,
+ });
+
+ factory SubmissionView({
+ required String id,
+ required _isc.UuidValue itemUuid,
+ required _igclxad1.SubmissionStatus status,
+ _ifp7j30v.SubmissionDeliveryState? delivery,
+ required String publicationId,
+ required String publicationName,
+ required String shownHost,
+ required bool verified,
+ required bool accepting,
+ required _i1a71r29.PublicationFields fields,
+ String? termsUrl,
+ required bool consentNeeded,
+ required _igthguer.SubmissionValues values,
+ String? publicUrl,
+ _isc.UuidValue? feedbackThreadUuid,
+ String? lastMessage,
+ DateTime? lastMessageAt,
+ int? lastSubmittedRevision,
+ required List<_idtrp1yl.SubmissionRevisionView> revisions,
+ required DateTime updatedAt,
+ }) = _SubmissionViewImpl;
+
+ factory SubmissionView.fromJson(Map jsonSerialization) {
+ return SubmissionView(
+ id: jsonSerialization['id'] as String,
+ itemUuid: _isc.UuidValueJsonExtension.fromJson(
+ jsonSerialization['itemUuid'],
+ ),
+ status: _igclxad1.SubmissionStatus.fromJson(
+ (jsonSerialization['status'] as String),
+ ),
+ delivery: jsonSerialization['delivery'] == null
+ ? null
+ : _ifp7j30v.SubmissionDeliveryState.fromJson(
+ (jsonSerialization['delivery'] as String),
+ ),
+ publicationId: jsonSerialization['publicationId'] as String,
+ publicationName: jsonSerialization['publicationName'] as String,
+ shownHost: jsonSerialization['shownHost'] as String,
+ verified: _isc.BoolJsonExtension.fromJson(jsonSerialization['verified']),
+ accepting: _isc.BoolJsonExtension.fromJson(
+ jsonSerialization['accepting'],
+ ),
+ fields: _i8pkqg1o.Protocol().deserialize<_i1a71r29.PublicationFields>(
+ jsonSerialization['fields'],
+ ),
+ termsUrl: jsonSerialization['termsUrl'] as String?,
+ consentNeeded: _isc.BoolJsonExtension.fromJson(
+ jsonSerialization['consentNeeded'],
+ ),
+ values: _i8pkqg1o.Protocol().deserialize<_igthguer.SubmissionValues>(
+ jsonSerialization['values'],
+ ),
+ publicUrl: jsonSerialization['publicUrl'] as String?,
+ feedbackThreadUuid: jsonSerialization['feedbackThreadUuid'] == null
+ ? null
+ : _isc.UuidValueJsonExtension.fromJson(
+ jsonSerialization['feedbackThreadUuid'],
+ ),
+ lastMessage: jsonSerialization['lastMessage'] as String?,
+ lastMessageAt: jsonSerialization['lastMessageAt'] == null
+ ? null
+ : _isc.DateTimeJsonExtension.fromJson(
+ jsonSerialization['lastMessageAt'],
+ ),
+ lastSubmittedRevision: jsonSerialization['lastSubmittedRevision'] as int?,
+ revisions: _i8pkqg1o.Protocol()
+ .deserialize>(
+ jsonSerialization['revisions'],
+ ),
+ updatedAt: _isc.DateTimeJsonExtension.fromJson(
+ jsonSerialization['updatedAt'],
+ ),
+ );
+ }
+
+ /// "sub_".
+ String id;
+
+ _isc.UuidValue itemUuid;
+
+ _igclxad1.SubmissionStatus status;
+
+ _ifp7j30v.SubmissionDeliveryState? delivery;
+
+ String publicationId;
+
+ String publicationName;
+
+ String shownHost;
+
+ bool verified;
+
+ /// The publication still takes submissions (enabled, caller not blocked).
+ bool accepting;
+
+ _i1a71r29.PublicationFields fields;
+
+ String? termsUrl;
+
+ /// The caller still has to agree to the terms before submitting.
+ bool consentNeeded;
+
+ _igthguer.SubmissionValues values;
+
+ String? publicUrl;
+
+ _isc.UuidValue? feedbackThreadUuid;
+
+ /// The publication's last message (Markdown) and when it came.
+ String? lastMessage;
+
+ DateTime? lastMessageAt;
+
+ /// The document revision of the last submit (null before the first).
+ int? lastSubmittedRevision;
+
+ List<_idtrp1yl.SubmissionRevisionView> revisions;
+
+ DateTime updatedAt;
+
+ /// Returns a shallow copy of this [SubmissionView]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ SubmissionView copyWith({
+ String? id,
+ _isc.UuidValue? itemUuid,
+ _igclxad1.SubmissionStatus? status,
+ _ifp7j30v.SubmissionDeliveryState? delivery,
+ String? publicationId,
+ String? publicationName,
+ String? shownHost,
+ bool? verified,
+ bool? accepting,
+ _i1a71r29.PublicationFields? fields,
+ String? termsUrl,
+ bool? consentNeeded,
+ _igthguer.SubmissionValues? values,
+ String? publicUrl,
+ _isc.UuidValue? feedbackThreadUuid = const _isc.$UndefinedUuidValue(),
+ String? lastMessage,
+ DateTime? lastMessageAt = const _isc.$UndefinedDateTime(),
+ int? lastSubmittedRevision,
+ List<_idtrp1yl.SubmissionRevisionView>? revisions,
+ DateTime? updatedAt,
+ });
+ @override
+ Map toJson() {
+ return {
+ '__className__': 'SubmissionView',
+ 'id': id,
+ 'itemUuid': itemUuid.toJson(),
+ 'status': status.toJson(),
+ if (delivery != null) 'delivery': delivery?.toJson(),
+ 'publicationId': publicationId,
+ 'publicationName': publicationName,
+ 'shownHost': shownHost,
+ 'verified': verified,
+ 'accepting': accepting,
+ 'fields': fields.toJson(),
+ if (termsUrl != null) 'termsUrl': termsUrl,
+ 'consentNeeded': consentNeeded,
+ 'values': values.toJson(),
+ if (publicUrl != null) 'publicUrl': publicUrl,
+ if (feedbackThreadUuid != null)
+ 'feedbackThreadUuid': feedbackThreadUuid?.toJson(),
+ if (lastMessage != null) 'lastMessage': lastMessage,
+ if (lastMessageAt != null) 'lastMessageAt': lastMessageAt?.toJson(),
+ if (lastSubmittedRevision != null)
+ 'lastSubmittedRevision': lastSubmittedRevision,
+ 'revisions': revisions.toJson(valueToJson: (v) => v.toJson()),
+ 'updatedAt': updatedAt.toJson(),
+ };
+ }
+
+ @override
+ Map toJsonForProtocol() {
+ return {
+ '__className__': 'SubmissionView',
+ 'id': id,
+ 'itemUuid': itemUuid.toJson(),
+ 'status': status.toJson(),
+ if (delivery != null) 'delivery': delivery?.toJson(),
+ 'publicationId': publicationId,
+ 'publicationName': publicationName,
+ 'shownHost': shownHost,
+ 'verified': verified,
+ 'accepting': accepting,
+ 'fields': fields.toJsonForProtocol(),
+ if (termsUrl != null) 'termsUrl': termsUrl,
+ 'consentNeeded': consentNeeded,
+ 'values': values.toJsonForProtocol(),
+ if (publicUrl != null) 'publicUrl': publicUrl,
+ if (feedbackThreadUuid != null)
+ 'feedbackThreadUuid': feedbackThreadUuid?.toJson(),
+ if (lastMessage != null) 'lastMessage': lastMessage,
+ if (lastMessageAt != null) 'lastMessageAt': lastMessageAt?.toJson(),
+ if (lastSubmittedRevision != null)
+ 'lastSubmittedRevision': lastSubmittedRevision,
+ 'revisions': revisions.toJson(valueToJson: (v) => v.toJsonForProtocol()),
+ 'updatedAt': updatedAt.toJson(),
+ };
+ }
+
+ @override
+ String toString() {
+ return _isc.SerializationManager.encode(this);
+ }
+}
+
+class _Undefined {}
+
+class _SubmissionViewImpl extends SubmissionView {
+ _SubmissionViewImpl({
+ required String id,
+ required _isc.UuidValue itemUuid,
+ required _igclxad1.SubmissionStatus status,
+ _ifp7j30v.SubmissionDeliveryState? delivery,
+ required String publicationId,
+ required String publicationName,
+ required String shownHost,
+ required bool verified,
+ required bool accepting,
+ required _i1a71r29.PublicationFields fields,
+ String? termsUrl,
+ required bool consentNeeded,
+ required _igthguer.SubmissionValues values,
+ String? publicUrl,
+ _isc.UuidValue? feedbackThreadUuid,
+ String? lastMessage,
+ DateTime? lastMessageAt,
+ int? lastSubmittedRevision,
+ required List<_idtrp1yl.SubmissionRevisionView> revisions,
+ required DateTime updatedAt,
+ }) : super._(
+ id: id,
+ itemUuid: itemUuid,
+ status: status,
+ delivery: delivery,
+ publicationId: publicationId,
+ publicationName: publicationName,
+ shownHost: shownHost,
+ verified: verified,
+ accepting: accepting,
+ fields: fields,
+ termsUrl: termsUrl,
+ consentNeeded: consentNeeded,
+ values: values,
+ publicUrl: publicUrl,
+ feedbackThreadUuid: feedbackThreadUuid,
+ lastMessage: lastMessage,
+ lastMessageAt: lastMessageAt,
+ lastSubmittedRevision: lastSubmittedRevision,
+ revisions: revisions,
+ updatedAt: updatedAt,
+ );
+
+ /// Returns a shallow copy of this [SubmissionView]
+ /// with some or all fields replaced by the given arguments.
+ @_isc.useResult
+ @override
+ SubmissionView copyWith({
+ String? id,
+ _isc.UuidValue? itemUuid,
+ _igclxad1.SubmissionStatus? status,
+ Object? delivery = _Undefined,
+ String? publicationId,
+ String? publicationName,
+ String? shownHost,
+ bool? verified,
+ bool? accepting,
+ _i1a71r29.PublicationFields? fields,
+ Object? termsUrl = _Undefined,
+ bool? consentNeeded,
+ _igthguer.SubmissionValues? values,
+ Object? publicUrl = _Undefined,
+ _isc.UuidValue? feedbackThreadUuid = const _isc.$UndefinedUuidValue(),
+ Object? lastMessage = _Undefined,
+ DateTime? lastMessageAt = const _isc.$UndefinedDateTime(),
+ Object? lastSubmittedRevision = _Undefined,
+ List<_idtrp1yl.SubmissionRevisionView>? revisions,
+ DateTime? updatedAt,
+ }) {
+ return SubmissionView(
+ id: id ?? this.id,
+ itemUuid: itemUuid ?? this.itemUuid,
+ status: status ?? this.status,
+ delivery: delivery is _ifp7j30v.SubmissionDeliveryState?
+ ? delivery
+ : this.delivery,
+ publicationId: publicationId ?? this.publicationId,
+ publicationName: publicationName ?? this.publicationName,
+ shownHost: shownHost ?? this.shownHost,
+ verified: verified ?? this.verified,
+ accepting: accepting ?? this.accepting,
+ fields: fields ?? this.fields.copyWith(),
+ termsUrl: termsUrl is String? ? termsUrl : this.termsUrl,
+ consentNeeded: consentNeeded ?? this.consentNeeded,
+ values: values ?? this.values.copyWith(),
+ publicUrl: publicUrl is String? ? publicUrl : this.publicUrl,
+ feedbackThreadUuid: feedbackThreadUuid is _isc.UndefinedSentinel
+ ? this.feedbackThreadUuid
+ : feedbackThreadUuid,
+ lastMessage: lastMessage is String? ? lastMessage : this.lastMessage,
+ lastMessageAt: lastMessageAt is _isc.UndefinedSentinel
+ ? this.lastMessageAt
+ : lastMessageAt,
+ lastSubmittedRevision: lastSubmittedRevision is int?
+ ? lastSubmittedRevision
+ : this.lastSubmittedRevision,
+ revisions:
+ revisions ?? this.revisions.map((e0) => e0.copyWith()).toList(),
+ updatedAt: updatedAt ?? this.updatedAt,
+ );
+ }
+}
diff --git a/wonky_client/lib/src/protocol/platform/sharing/drive_item_permission.dart b/wonky_client/lib/src/protocol/platform/sharing/drive_item_permission.dart
index 58631b1b..c8f78458 100644
--- a/wonky_client/lib/src/protocol/platform/sharing/drive_item_permission.dart
+++ b/wonky_client/lib/src/protocol/platform/sharing/drive_item_permission.dart
@@ -27,6 +27,7 @@ abstract class DriveItemPermission
required this.grantedById,
required this.grantedAt,
this.expiresAt,
+ this.viaPublicationId,
});
factory DriveItemPermission({
@@ -39,6 +40,7 @@ abstract class DriveItemPermission
required int grantedById,
required DateTime grantedAt,
DateTime? expiresAt,
+ int? viaPublicationId,
}) = _DriveItemPermissionImpl;
factory DriveItemPermission.fromJson(Map jsonSerialization) {
@@ -58,6 +60,7 @@ abstract class DriveItemPermission
expiresAt: jsonSerialization['expiresAt'] == null
? null
: _isc.DateTimeJsonExtension.fromJson(jsonSerialization['expiresAt']),
+ viaPublicationId: jsonSerialization['viaPublicationId'] as int?,
);
}
@@ -84,6 +87,9 @@ abstract class DriveItemPermission
DateTime? expiresAt;
+ /// (M14) Moderator grant of a submission (specs/18 §5.1); removed with it.
+ int? viaPublicationId;
+
/// Returns a shallow copy of this [DriveItemPermission]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@@ -97,6 +103,7 @@ abstract class DriveItemPermission
int? grantedById,
DateTime? grantedAt,
DateTime? expiresAt = const _isc.$UndefinedDateTime(),
+ int? viaPublicationId,
});
@override
Map toJson() {
@@ -111,6 +118,7 @@ abstract class DriveItemPermission
'grantedById': grantedById,
'grantedAt': grantedAt.toJson(),
if (expiresAt != null) 'expiresAt': expiresAt?.toJson(),
+ if (viaPublicationId != null) 'viaPublicationId': viaPublicationId,
};
}
@@ -127,6 +135,7 @@ abstract class DriveItemPermission
'grantedById': grantedById,
'grantedAt': grantedAt.toJson(),
if (expiresAt != null) 'expiresAt': expiresAt?.toJson(),
+ if (viaPublicationId != null) 'viaPublicationId': viaPublicationId,
};
}
@@ -149,6 +158,7 @@ class _DriveItemPermissionImpl extends DriveItemPermission {
required int grantedById,
required DateTime grantedAt,
DateTime? expiresAt,
+ int? viaPublicationId,
}) : super._(
id: id,
itemId: itemId,
@@ -159,6 +169,7 @@ class _DriveItemPermissionImpl extends DriveItemPermission {
grantedById: grantedById,
grantedAt: grantedAt,
expiresAt: expiresAt,
+ viaPublicationId: viaPublicationId,
);
/// Returns a shallow copy of this [DriveItemPermission]
@@ -175,6 +186,7 @@ class _DriveItemPermissionImpl extends DriveItemPermission {
int? grantedById,
DateTime? grantedAt,
DateTime? expiresAt = const _isc.$UndefinedDateTime(),
+ Object? viaPublicationId = _Undefined,
}) {
return DriveItemPermission(
id: id is int? ? id : this.id,
@@ -190,6 +202,9 @@ class _DriveItemPermissionImpl extends DriveItemPermission {
expiresAt: expiresAt is _isc.UndefinedSentinel
? this.expiresAt
: expiresAt,
+ viaPublicationId: viaPublicationId is int?
+ ? viaPublicationId
+ : this.viaPublicationId,
);
}
}
diff --git a/wonky_client/lib/src/protocol/platform/sharing/permission_view.dart b/wonky_client/lib/src/protocol/platform/sharing/permission_view.dart
index bdf37bd9..e56a2abc 100644
--- a/wonky_client/lib/src/protocol/platform/sharing/permission_view.dart
+++ b/wonky_client/lib/src/protocol/platform/sharing/permission_view.dart
@@ -25,6 +25,7 @@ abstract class PermissionView
this.inheritedFromName,
required this.grantedAt,
this.expiresAt,
+ this.viaPublication,
});
factory PermissionView({
@@ -35,6 +36,7 @@ abstract class PermissionView
String? inheritedFromName,
required DateTime grantedAt,
DateTime? expiresAt,
+ String? viaPublication,
}) = _PermissionViewImpl;
factory PermissionView.fromJson(Map jsonSerialization) {
@@ -60,6 +62,7 @@ abstract class PermissionView
expiresAt: jsonSerialization['expiresAt'] == null
? null
: _isc.DateTimeJsonExtension.fromJson(jsonSerialization['expiresAt']),
+ viaPublication: jsonSerialization['viaPublication'] as String?,
);
}
@@ -79,6 +82,9 @@ abstract class PermissionView
DateTime? expiresAt;
+ /// (M14) The publication a moderator grant comes from (specs/18 §5.1).
+ String? viaPublication;
+
/// Returns a shallow copy of this [PermissionView]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@@ -90,6 +96,7 @@ abstract class PermissionView
String? inheritedFromName,
DateTime? grantedAt,
DateTime? expiresAt = const _isc.$UndefinedDateTime(),
+ String? viaPublication,
});
@override
Map toJson() {
@@ -103,6 +110,7 @@ abstract class PermissionView
if (inheritedFromName != null) 'inheritedFromName': inheritedFromName,
'grantedAt': grantedAt.toJson(),
if (expiresAt != null) 'expiresAt': expiresAt?.toJson(),
+ if (viaPublication != null) 'viaPublication': viaPublication,
};
}
@@ -118,6 +126,7 @@ abstract class PermissionView
if (inheritedFromName != null) 'inheritedFromName': inheritedFromName,
'grantedAt': grantedAt.toJson(),
if (expiresAt != null) 'expiresAt': expiresAt?.toJson(),
+ if (viaPublication != null) 'viaPublication': viaPublication,
};
}
@@ -143,6 +152,7 @@ class _PermissionViewImpl extends PermissionView {
String? inheritedFromName,
required DateTime grantedAt,
DateTime? expiresAt,
+ String? viaPublication,
}) : super._(
user: user,
invitedEmail: invitedEmail,
@@ -151,6 +161,7 @@ class _PermissionViewImpl extends PermissionView {
inheritedFromName: inheritedFromName,
grantedAt: grantedAt,
expiresAt: expiresAt,
+ viaPublication: viaPublication,
);
/// Returns a shallow copy of this [PermissionView]
@@ -165,6 +176,7 @@ class _PermissionViewImpl extends PermissionView {
Object? inheritedFromName = _Undefined,
DateTime? grantedAt,
DateTime? expiresAt = const _isc.$UndefinedDateTime(),
+ Object? viaPublication = _Undefined,
}) {
return PermissionView(
user: user is _isc.UndefinedSentinel ? this.user?.copyWith() : user,
@@ -180,6 +192,9 @@ class _PermissionViewImpl extends PermissionView {
expiresAt: expiresAt is _isc.UndefinedSentinel
? this.expiresAt
: expiresAt,
+ viaPublication: viaPublication is String?
+ ? viaPublication
+ : this.viaPublication,
);
}
}
diff --git a/wonky_client/lib/src/protocol/protocol.dart b/wonky_client/lib/src/protocol/protocol.dart
index 808f32a1..93e58340 100644
--- a/wonky_client/lib/src/protocol/protocol.dart
+++ b/wonky_client/lib/src/protocol/protocol.dart
@@ -38,6 +38,10 @@ import 'package:wonky_client/src/protocol/platform/drive/template_view.dart'
as _i1pzk6fp;
import 'package:wonky_client/src/protocol/platform/files/file_url_view.dart'
as _i4i1hokq;
+import 'package:wonky_client/src/protocol/platform/publications/models/publication_delivery_view.dart'
+ as _ipl2ldhs;
+import 'package:wonky_client/src/protocol/platform/publications/models/publication_settings.dart'
+ as _i9bruko9;
import 'package:wonky_client/src/protocol/platform/revlog/models/rev_op_view.dart'
as _iq15rk0l;
import 'package:wonky_client/src/protocol/platform/users/access_token_view.dart'
@@ -197,6 +201,28 @@ import 'platform/presence/participant.dart' as _iylgyc33;
import 'platform/presence/presence_event.dart' as _i8r9afjv;
import 'platform/presence/presence_state.dart' as _ik055xb9;
import 'platform/presence/presence_update.dart' as _isz2sju0;
+import 'platform/publications/models/publication_card.dart' as _i9g6l3wu;
+import 'platform/publications/models/publication_check_result.dart'
+ as _i0a1sw0a;
+import 'platform/publications/models/publication_delivery_view.dart'
+ as _iz1gego0;
+import 'platform/publications/models/publication_field_mode.dart' as _iucp51pg;
+import 'platform/publications/models/publication_fields.dart' as _i63wbtw0;
+import 'platform/publications/models/publication_input.dart' as _iv6bey6s;
+import 'platform/publications/models/publication_moderator_view.dart'
+ as _ij9tsbn9;
+import 'platform/publications/models/publication_open_submission.dart'
+ as _ivvukfve;
+import 'platform/publications/models/publication_secret.dart' as _iyqlj2kr;
+import 'platform/publications/models/publication_settings.dart' as _i5i48220;
+import 'platform/publications/models/submission_delivery_state.dart'
+ as _ip6l83ta;
+import 'platform/publications/models/submission_revision_view.dart'
+ as _iegijpud;
+import 'platform/publications/models/submission_started.dart' as _ijb8v3k4;
+import 'platform/publications/models/submission_status.dart' as _i7hbulg4;
+import 'platform/publications/models/submission_values.dart' as _iq9uje8l;
+import 'platform/publications/models/submission_view.dart' as _iakndug7;
import 'platform/revlog/messages/rev_client_message.dart' as _i6ck957q;
import 'platform/revlog/messages/rev_server_message.dart' as _ie6clghy;
import 'platform/revlog/models/rev_file_info.dart' as _ihwy023l;
@@ -450,6 +476,22 @@ export 'platform/presence/participant.dart';
export 'platform/presence/presence_event.dart';
export 'platform/presence/presence_state.dart';
export 'platform/presence/presence_update.dart';
+export 'platform/publications/models/publication_card.dart';
+export 'platform/publications/models/publication_check_result.dart';
+export 'platform/publications/models/publication_delivery_view.dart';
+export 'platform/publications/models/publication_field_mode.dart';
+export 'platform/publications/models/publication_fields.dart';
+export 'platform/publications/models/publication_input.dart';
+export 'platform/publications/models/publication_moderator_view.dart';
+export 'platform/publications/models/publication_open_submission.dart';
+export 'platform/publications/models/publication_secret.dart';
+export 'platform/publications/models/publication_settings.dart';
+export 'platform/publications/models/submission_delivery_state.dart';
+export 'platform/publications/models/submission_revision_view.dart';
+export 'platform/publications/models/submission_started.dart';
+export 'platform/publications/models/submission_status.dart';
+export 'platform/publications/models/submission_values.dart';
+export 'platform/publications/models/submission_view.dart';
export 'platform/revlog/messages/rev_client_message.dart';
export 'platform/revlog/messages/rev_server_message.dart';
export 'platform/revlog/models/rev_file_info.dart';
@@ -730,6 +772,22 @@ class Protocol extends _isc.SerializationManager
_isc.getType<_i8r9afjv.PresenceUpdated>,
_isc.getType<_ik055xb9.PresenceState>,
_isc.getType<_isz2sju0.PresenceUpdate>,
+ _isc.getType<_i9g6l3wu.PublicationCard>,
+ _isc.getType<_i0a1sw0a.PublicationCheckResult>,
+ _isc.getType<_iz1gego0.PublicationDeliveryView>,
+ _isc.getType<_iucp51pg.PublicationFieldMode>,
+ _isc.getType<_i63wbtw0.PublicationFields>,
+ _isc.getType<_iv6bey6s.PublicationInput>,
+ _isc.getType<_ij9tsbn9.PublicationModeratorView>,
+ _isc.getType<_ivvukfve.PublicationOpenSubmission>,
+ _isc.getType<_iyqlj2kr.PublicationSecret>,
+ _isc.getType<_i5i48220.PublicationSettings>,
+ _isc.getType<_ip6l83ta.SubmissionDeliveryState>,
+ _isc.getType<_iegijpud.SubmissionRevisionView>,
+ _isc.getType<_ijb8v3k4.SubmissionStarted>,
+ _isc.getType<_i7hbulg4.SubmissionStatus>,
+ _isc.getType<_iq9uje8l.SubmissionValues>,
+ _isc.getType<_iakndug7.SubmissionView>,
_isc.getType<_ie6clghy.RevAck>,
_isc.getType<_i6ck957q.RevPing>,
_isc.getType<_i6ck957q.RevRequestOps>,
@@ -1030,6 +1088,22 @@ class Protocol extends _isc.SerializationManager
_isc.getType<_i8r9afjv.PresenceUpdated?>,
_isc.getType<_ik055xb9.PresenceState?>,
_isc.getType<_isz2sju0.PresenceUpdate?>,
+ _isc.getType<_i9g6l3wu.PublicationCard?>,
+ _isc.getType<_i0a1sw0a.PublicationCheckResult?>,
+ _isc.getType<_iz1gego0.PublicationDeliveryView?>,
+ _isc.getType<_iucp51pg.PublicationFieldMode?>,
+ _isc.getType<_i63wbtw0.PublicationFields?>,
+ _isc.getType<_iv6bey6s.PublicationInput?>,
+ _isc.getType<_ij9tsbn9.PublicationModeratorView?>,
+ _isc.getType<_ivvukfve.PublicationOpenSubmission?>,
+ _isc.getType<_iyqlj2kr.PublicationSecret?>,
+ _isc.getType<_i5i48220.PublicationSettings?>,
+ _isc.getType<_ip6l83ta.SubmissionDeliveryState?>,
+ _isc.getType<_iegijpud.SubmissionRevisionView?>,
+ _isc.getType<_ijb8v3k4.SubmissionStarted?>,
+ _isc.getType<_i7hbulg4.SubmissionStatus?>,
+ _isc.getType<_iq9uje8l.SubmissionValues?>,
+ _isc.getType<_iakndug7.SubmissionView?>,
_isc.getType<_ie6clghy.RevAck?>,
_isc.getType<_i6ck957q.RevPing?>,
_isc.getType<_i6ck957q.RevRequestOps?>,
@@ -1220,6 +1294,9 @@ class Protocol extends _isc.SerializationManager
_isc.getType>,
_isc.getType>,
_isc.getType>,
+ _isc.getType>,
+ _isc.getType>,
+ _isc.getType>,
_isc.getType>,
_isc.getType?>,
_isc.getType>,
@@ -1257,7 +1334,9 @@ class Protocol extends _isc.SerializationManager
_isc.getType>,
_isc.getType>,
_isc.getType>,
+ _isc.getType>,
_isc.getType>,
+ _isc.getType>,
_isc.getType>,
_isc.getType>,
_isc.getType>,
@@ -1728,6 +1807,54 @@ class Protocol extends _isc.SerializationManager
if (t == _isz2sju0.PresenceUpdate) {
return _isz2sju0.PresenceUpdate.fromJson(data) as T;
}
+ if (t == _i9g6l3wu.PublicationCard) {
+ return _i9g6l3wu.PublicationCard.fromJson(data) as T;
+ }
+ if (t == _i0a1sw0a.PublicationCheckResult) {
+ return _i0a1sw0a.PublicationCheckResult.fromJson(data) as T;
+ }
+ if (t == _iz1gego0.PublicationDeliveryView) {
+ return _iz1gego0.PublicationDeliveryView.fromJson(data) as T;
+ }
+ if (t == _iucp51pg.PublicationFieldMode) {
+ return _iucp51pg.PublicationFieldMode.fromJson(data) as T;
+ }
+ if (t == _i63wbtw0.PublicationFields) {
+ return _i63wbtw0.PublicationFields.fromJson(data) as T;
+ }
+ if (t == _iv6bey6s.PublicationInput) {
+ return _iv6bey6s.PublicationInput.fromJson(data) as T;
+ }
+ if (t == _ij9tsbn9.PublicationModeratorView) {
+ return _ij9tsbn9.PublicationModeratorView.fromJson(data) as T;
+ }
+ if (t == _ivvukfve.PublicationOpenSubmission) {
+ return _ivvukfve.PublicationOpenSubmission.fromJson(data) as T;
+ }
+ if (t == _iyqlj2kr.PublicationSecret) {
+ return _iyqlj2kr.PublicationSecret.fromJson(data) as T;
+ }
+ if (t == _i5i48220.PublicationSettings) {
+ return _i5i48220.PublicationSettings.fromJson(data) as T;
+ }
+ if (t == _ip6l83ta.SubmissionDeliveryState) {
+ return _ip6l83ta.SubmissionDeliveryState.fromJson(data) as T;
+ }
+ if (t == _iegijpud.SubmissionRevisionView) {
+ return _iegijpud.SubmissionRevisionView.fromJson(data) as T;
+ }
+ if (t == _ijb8v3k4.SubmissionStarted) {
+ return _ijb8v3k4.SubmissionStarted.fromJson(data) as T;
+ }
+ if (t == _i7hbulg4.SubmissionStatus) {
+ return _i7hbulg4.SubmissionStatus.fromJson(data) as T;
+ }
+ if (t == _iq9uje8l.SubmissionValues) {
+ return _iq9uje8l.SubmissionValues.fromJson(data) as T;
+ }
+ if (t == _iakndug7.SubmissionView) {
+ return _iakndug7.SubmissionView.fromJson(data) as T;
+ }
if (t == _ie6clghy.RevAck) {
return _ie6clghy.RevAck.fromJson(data) as T;
}
@@ -2746,6 +2873,86 @@ class Protocol extends _isc.SerializationManager
return (data != null ? _isz2sju0.PresenceUpdate.fromJson(data) : null)
as T;
}
+ if (t == _isc.getType<_i9g6l3wu.PublicationCard?>()) {
+ return (data != null ? _i9g6l3wu.PublicationCard.fromJson(data) : null)
+ as T;
+ }
+ if (t == _isc.getType<_i0a1sw0a.PublicationCheckResult?>()) {
+ return (data != null
+ ? _i0a1sw0a.PublicationCheckResult.fromJson(data)
+ : null)
+ as T;
+ }
+ if (t == _isc.getType<_iz1gego0.PublicationDeliveryView?>()) {
+ return (data != null
+ ? _iz1gego0.PublicationDeliveryView.fromJson(data)
+ : null)
+ as T;
+ }
+ if (t == _isc.getType<_iucp51pg.PublicationFieldMode?>()) {
+ return (data != null
+ ? _iucp51pg.PublicationFieldMode.fromJson(data)
+ : null)
+ as T;
+ }
+ if (t == _isc.getType<_i63wbtw0.PublicationFields?>()) {
+ return (data != null ? _i63wbtw0.PublicationFields.fromJson(data) : null)
+ as T;
+ }
+ if (t == _isc.getType<_iv6bey6s.PublicationInput?>()) {
+ return (data != null ? _iv6bey6s.PublicationInput.fromJson(data) : null)
+ as T;
+ }
+ if (t == _isc.getType<_ij9tsbn9.PublicationModeratorView?>()) {
+ return (data != null
+ ? _ij9tsbn9.PublicationModeratorView.fromJson(data)
+ : null)
+ as T;
+ }
+ if (t == _isc.getType<_ivvukfve.PublicationOpenSubmission?>()) {
+ return (data != null
+ ? _ivvukfve.PublicationOpenSubmission.fromJson(data)
+ : null)
+ as T;
+ }
+ if (t == _isc.getType<_iyqlj2kr.PublicationSecret?>()) {
+ return (data != null ? _iyqlj2kr.PublicationSecret.fromJson(data) : null)
+ as T;
+ }
+ if (t == _isc.getType<_i5i48220.PublicationSettings?>()) {
+ return (data != null
+ ? _i5i48220.PublicationSettings.fromJson(data)
+ : null)
+ as T;
+ }
+ if (t == _isc.getType<_ip6l83ta.SubmissionDeliveryState?>()) {
+ return (data != null
+ ? _ip6l83ta.SubmissionDeliveryState.fromJson(data)
+ : null)
+ as T;
+ }
+ if (t == _isc.getType<_iegijpud.SubmissionRevisionView?>()) {
+ return (data != null
+ ? _iegijpud.SubmissionRevisionView.fromJson(data)
+ : null)
+ as T;
+ }
+ if (t == _isc.getType<_ijb8v3k4.SubmissionStarted?>()) {
+ return (data != null ? _ijb8v3k4.SubmissionStarted.fromJson(data) : null)
+ as T;
+ }
+ if (t == _isc.getType<_i7hbulg4.SubmissionStatus?>()) {
+ return (data != null ? _i7hbulg4.SubmissionStatus.fromJson(data) : null)
+ as T;
+ }
+ if (t == _isc.getType<_iq9uje8l.SubmissionValues?>()) {
+ return (data != null ? _iq9uje8l.SubmissionValues.fromJson(data) : null)
+ as T;
+ }
+ if (t == _isc.getType<_iakndug7.SubmissionView?>()) {
+ return (data != null ? _iakndug7.SubmissionView.fromJson(data) : null)
+ as T;
+ }
if (t == _isc.getType<_ie6clghy.RevAck?>()) {
return (data != null ? _ie6clghy.RevAck.fromJson(data) : null) as T;
}
@@ -3630,6 +3837,24 @@ class Protocol extends _isc.SerializationManager
.toList()
as T;
}
+ if (t == List<_ivvukfve.PublicationOpenSubmission>) {
+ return (data as List)
+ .map((e) => deserialize<_ivvukfve.PublicationOpenSubmission>(e))
+ .toList()
+ as T;
+ }
+ if (t == List<_ij9tsbn9.PublicationModeratorView>) {
+ return (data as List)
+ .map((e) => deserialize<_ij9tsbn9.PublicationModeratorView>(e))
+ .toList()
+ as T;
+ }
+ if (t == List<_iegijpud.SubmissionRevisionView>) {
+ return (data as List)
+ .map((e) => deserialize<_iegijpud.SubmissionRevisionView>(e))
+ .toList()
+ as T;
+ }
if (t == List<_it4o2pk5.RevOpView>) {
return (data as List)
.map((e) => deserialize<_it4o2pk5.RevOpView>(e))
@@ -3859,9 +4084,21 @@ class Protocol extends _isc.SerializationManager
.toList()
as T;
}
+ if (t == List<_i9bruko9.PublicationSettings>) {
+ return (data as List)
+ .map((e) => deserialize<_i9bruko9.PublicationSettings>(e))
+ .toList()
+ as T;
+ }
if (t == List) {
return (data as List).map((e) => deserialize(e)).toList() as T;
}
+ if (t == List<_ipl2ldhs.PublicationDeliveryView>) {
+ return (data as List)
+ .map((e) => deserialize<_ipl2ldhs.PublicationDeliveryView>(e))
+ .toList()
+ as T;
+ }
if (t == List<_ip6swqsj.AccessTokenView>) {
return (data as List)
.map((e) => deserialize<_ip6swqsj.AccessTokenView>(e))
@@ -4129,6 +4366,22 @@ class Protocol extends _isc.SerializationManager
_i8r9afjv.PresenceUpdated => 'PresenceUpdated',
_ik055xb9.PresenceState => 'PresenceState',
_isz2sju0.PresenceUpdate => 'PresenceUpdate',
+ _i9g6l3wu.PublicationCard => 'PublicationCard',
+ _i0a1sw0a.PublicationCheckResult => 'PublicationCheckResult',
+ _iz1gego0.PublicationDeliveryView => 'PublicationDeliveryView',
+ _iucp51pg.PublicationFieldMode => 'PublicationFieldMode',
+ _i63wbtw0.PublicationFields => 'PublicationFields',
+ _iv6bey6s.PublicationInput => 'PublicationInput',
+ _ij9tsbn9.PublicationModeratorView => 'PublicationModeratorView',
+ _ivvukfve.PublicationOpenSubmission => 'PublicationOpenSubmission',
+ _iyqlj2kr.PublicationSecret => 'PublicationSecret',
+ _i5i48220.PublicationSettings => 'PublicationSettings',
+ _ip6l83ta.SubmissionDeliveryState => 'SubmissionDeliveryState',
+ _iegijpud.SubmissionRevisionView => 'SubmissionRevisionView',
+ _ijb8v3k4.SubmissionStarted => 'SubmissionStarted',
+ _i7hbulg4.SubmissionStatus => 'SubmissionStatus',
+ _iq9uje8l.SubmissionValues => 'SubmissionValues',
+ _iakndug7.SubmissionView => 'SubmissionView',
_ie6clghy.RevAck => 'RevAck',
_i6ck957q.RevPing => 'RevPing',
_i6ck957q.RevRequestOps => 'RevRequestOps',
@@ -4582,6 +4835,38 @@ class Protocol extends _isc.SerializationManager
return 'PresenceState';
case _isz2sju0.PresenceUpdate():
return 'PresenceUpdate';
+ case _i9g6l3wu.PublicationCard():
+ return 'PublicationCard';
+ case _i0a1sw0a.PublicationCheckResult():
+ return 'PublicationCheckResult';
+ case _iz1gego0.PublicationDeliveryView():
+ return 'PublicationDeliveryView';
+ case _iucp51pg.PublicationFieldMode():
+ return 'PublicationFieldMode';
+ case _i63wbtw0.PublicationFields():
+ return 'PublicationFields';
+ case _iv6bey6s.PublicationInput():
+ return 'PublicationInput';
+ case _ij9tsbn9.PublicationModeratorView():
+ return 'PublicationModeratorView';
+ case _ivvukfve.PublicationOpenSubmission():
+ return 'PublicationOpenSubmission';
+ case _iyqlj2kr.PublicationSecret():
+ return 'PublicationSecret';
+ case _i5i48220.PublicationSettings():
+ return 'PublicationSettings';
+ case _ip6l83ta.SubmissionDeliveryState():
+ return 'SubmissionDeliveryState';
+ case _iegijpud.SubmissionRevisionView():
+ return 'SubmissionRevisionView';
+ case _ijb8v3k4.SubmissionStarted():
+ return 'SubmissionStarted';
+ case _i7hbulg4.SubmissionStatus():
+ return 'SubmissionStatus';
+ case _iq9uje8l.SubmissionValues():
+ return 'SubmissionValues';
+ case _iakndug7.SubmissionView():
+ return 'SubmissionView';
case _ie6clghy.RevAck():
return 'RevAck';
case _i6ck957q.RevPing():
@@ -5343,6 +5628,54 @@ class Protocol extends _isc.SerializationManager
if (dataClassName == 'PresenceUpdate') {
return deserialize<_isz2sju0.PresenceUpdate>(data['data']);
}
+ if (dataClassName == 'PublicationCard') {
+ return deserialize<_i9g6l3wu.PublicationCard>(data['data']);
+ }
+ if (dataClassName == 'PublicationCheckResult') {
+ return deserialize<_i0a1sw0a.PublicationCheckResult>(data['data']);
+ }
+ if (dataClassName == 'PublicationDeliveryView') {
+ return deserialize<_iz1gego0.PublicationDeliveryView>(data['data']);
+ }
+ if (dataClassName == 'PublicationFieldMode') {
+ return deserialize<_iucp51pg.PublicationFieldMode>(data['data']);
+ }
+ if (dataClassName == 'PublicationFields') {
+ return deserialize<_i63wbtw0.PublicationFields>(data['data']);
+ }
+ if (dataClassName == 'PublicationInput') {
+ return deserialize<_iv6bey6s.PublicationInput>(data['data']);
+ }
+ if (dataClassName == 'PublicationModeratorView') {
+ return deserialize<_ij9tsbn9.PublicationModeratorView>(data['data']);
+ }
+ if (dataClassName == 'PublicationOpenSubmission') {
+ return deserialize<_ivvukfve.PublicationOpenSubmission>(data['data']);
+ }
+ if (dataClassName == 'PublicationSecret') {
+ return deserialize<_iyqlj2kr.PublicationSecret>(data['data']);
+ }
+ if (dataClassName == 'PublicationSettings') {
+ return deserialize<_i5i48220.PublicationSettings>(data['data']);
+ }
+ if (dataClassName == 'SubmissionDeliveryState') {
+ return deserialize<_ip6l83ta.SubmissionDeliveryState>(data['data']);
+ }
+ if (dataClassName == 'SubmissionRevisionView') {
+ return deserialize<_iegijpud.SubmissionRevisionView>(data['data']);
+ }
+ if (dataClassName == 'SubmissionStarted') {
+ return deserialize<_ijb8v3k4.SubmissionStarted>(data['data']);
+ }
+ if (dataClassName == 'SubmissionStatus') {
+ return deserialize<_i7hbulg4.SubmissionStatus>(data['data']);
+ }
+ if (dataClassName == 'SubmissionValues') {
+ return deserialize<_iq9uje8l.SubmissionValues>(data['data']);
+ }
+ if (dataClassName == 'SubmissionView') {
+ return deserialize<_iakndug7.SubmissionView>(data['data']);
+ }
if (dataClassName == 'RevAck') {
return deserialize<_ie6clghy.RevAck>(data['data']);
}
diff --git a/wonky_server/lib/server.dart b/wonky_server/lib/server.dart
index e35c3b03..0272c879 100644
--- a/wonky_server/lib/server.dart
+++ b/wonky_server/lib/server.dart
@@ -30,6 +30,7 @@ import 'src/web/routes/app_config_route.dart';
import 'src/web/routes/github_webhook_route.dart';
import 'src/web/routes/improvements_redirect_route.dart';
import 'src/web/routes/invite_route.dart';
+import 'src/web/routes/publication_api_route.dart';
import 'src/web/routes/root_redirect_route.dart';
import 'src/web/web_apps.dart';
import 'src/write/themes/theme_service.dart';
@@ -177,6 +178,8 @@ void _configureWebServer(Serverpod pod) {
// Webhooks of the Bugs & improvements GitHub App (specs/16 §13.3, README
// in lib/src/platform/board/github/).
pod.webServer.addRoute(GithubWebhookRoute(), '/api/github/webhook');
+ // The API blogs call with their publication's key (specs/18 §7.3).
+ pod.webServer.addRoute(PublicationApiRoute(), '/api/pub/v1/**');
// Wonky Drive file bytes (Drive 02 §3.3, §8; suite 13 §6): token-bound
// downloads with forced headers and the PUT upload route.
FileRoutes.register(pod.webServer);
@@ -293,6 +296,12 @@ final _jobs = {
'*/10 * * * *',
(c, id, cron) => c.callRecurring(identifier: id).cron(cron).board.sweep(),
),
+ // Publication webhooks due for a retry (specs/18 §7.2).
+ 'wonky.publicationSweep': (
+ '* * * * *',
+ (c, id, cron) =>
+ c.callRecurring(identifier: id).cron(cron).publications.sweep(),
+ ),
};
/// Brings the scheduled recurring jobs in line with [_jobs] (see
diff --git a/wonky_server/lib/src/generated/endpoints.dart b/wonky_server/lib/src/generated/endpoints.dart
index 75916fa4..ebd3c519 100644
--- a/wonky_server/lib/src/generated/endpoints.dart
+++ b/wonky_server/lib/src/generated/endpoints.dart
@@ -72,6 +72,10 @@ import 'package:wonky_server/src/generated/platform/mcp/ui_bridge/ui_bridge_mess
as _is78z0de;
import 'package:wonky_server/src/generated/platform/presence/presence_update.dart'
as _ipd65of1;
+import 'package:wonky_server/src/generated/platform/publications/models/publication_input.dart'
+ as _iy7hmxv3;
+import 'package:wonky_server/src/generated/platform/publications/models/submission_values.dart'
+ as _iwukrrmj;
import 'package:wonky_server/src/generated/platform/revlog/messages/rev_client_message.dart'
as _iv4mnr49;
import 'package:wonky_server/src/generated/platform/revlog/models/rev_page_request.dart'
@@ -116,6 +120,8 @@ import '../platform/mcp/ui_bridge/ui_bridge_endpoint.dart' as _ipb16bxt;
import '../platform/meta/meta_endpoint.dart' as _isistrmv;
import '../platform/notifications/notification_endpoint.dart' as _ia40tpv4;
import '../platform/presence/presence_endpoint.dart' as _iv7snmpw;
+import '../platform/publications/publication_endpoint.dart' as _igj7692v;
+import '../platform/publications/submission_endpoint.dart' as _i7uk3p1b;
import '../platform/sharing/sharing_endpoint.dart' as _itz30x9e;
import '../platform/users/access_token_endpoint.dart' as _iusd4uiv;
import '../platform/users/google_idp_endpoint.dart' as _i3henulz;
@@ -229,6 +235,18 @@ class Endpoints extends _is.EndpointDispatch {
'presence',
null,
),
+ 'publication': _igj7692v.PublicationEndpoint()
+ ..initialize(
+ server,
+ 'publication',
+ null,
+ ),
+ 'submission': _i7uk3p1b.SubmissionEndpoint()
+ ..initialize(
+ server,
+ 'submission',
+ null,
+ ),
'sharing': _itz30x9e.SharingEndpoint()
..initialize(
server,
@@ -3977,6 +3995,410 @@ class Endpoints extends _is.EndpointDispatch {
),
},
);
+ connectors['publication'] = _is.EndpointConnector(
+ name: 'publication',
+ endpoint: endpoints['publication']!,
+ methodConnectors: {
+ 'resolve': _is.MethodConnector(
+ name: 'resolve',
+ params: {
+ 'publicId': _is.ParameterDescription(
+ name: 'publicId',
+ type: _is.getType(),
+ nullable: false,
+ ),
+ },
+ call:
+ (
+ _is.Session session,
+ Map params,
+ ) async =>
+ (endpoints['publication'] as _igj7692v.PublicationEndpoint)
+ .resolve(
+ session,
+ params['publicId'],
+ ),
+ ),
+ 'startSubmission': _is.MethodConnector(
+ name: 'startSubmission',
+ params: {
+ 'publicId': _is.ParameterDescription(
+ name: 'publicId',
+ type: _is.getType(),
+ nullable: false,
+ ),
+ 'ref': _is.ParameterDescription(
+ name: 'ref',
+ type: _is.getType(),
+ nullable: true,
+ ),
+ 'agreed': _is.ParameterDescription(
+ name: 'agreed',
+ type: _is.getType(),
+ nullable: false,
+ ),
+ },
+ call:
+ (
+ _is.Session session,
+ Map