From 19773347c203b5b2fefeb1bbce3c3d8cd6683367 Mon Sep 17 00:00:00 2001 From: Frank Karlitschek Date: Mon, 14 Sep 2026 11:56:36 +0200 Subject: [PATCH 1/2] Files as attachments A post could carry pictures, video and audio and nothing else -- on a Nextcloud, where the file people want to share is the one thing that is already there. The allowlist gains the document kinds people actually share (PDF, plain text, Markdown, CSV, ZIP, EPUB, ODF and Office), and nothing a browser would run: no HTML, SVG or scripts. The picture ceiling (max_size) applies; a file is stored as it is, served as a download. On the client entity a file is Mastodon's `unknown` type with no preview of itself (the file used to be handed back as its own preview, which had a client try to draw a PDF), and when it was uploaded without a description it is named after itself -- the one field the entity has for saying what a file is. On the wire it is an ActivityPub Document with its mime, which Mastodon renders as a link and another Nextcloud as a file. The composer accepts, picks (the Files picker now offers them too) and takes dropped files of these kinds; a post shows a file as a named download card under its pictures, never in the mosaic or the viewer. Signed-off-by: Frank Karlitschek Co-Authored-By: Claude Fable 5.1 --- README.md | 3 +- docs/API.md | 2 +- docs/Architecture.md | 2 +- lib/Controller/ApiController.php | 13 ++- lib/Model/ActivityPub/Object/Document.php | 10 +- lib/Model/Client/MediaAttachment.php | 7 ++ lib/Service/CacheDocumentService.php | 30 +++++- lib/Service/InstanceService.php | 7 ++ src/components/Composer/Composer.vue | 34 ++++++- src/components/MediaAttachment.vue | 91 ++++++++++++++++++- src/components/PostAttachment.vue | 57 +++++++++--- tests/Controller/ApiControllerTest.php | 1 + .../Model/ActivityPub/Object/DocumentTest.php | 22 +++++ tests/Model/Client/MediaAttachmentTest.php | 1 + tests/Service/CacheDocumentServiceTest.php | 22 ++++- tests/js/components/Composer.test.js | 8 +- tests/js/components/MediaAttachment.test.js | 25 +++++ tests/js/components/PostAttachment.test.js | 20 ++++ 18 files changed, 320 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index d2c93f794..ec0426be8 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ It is a partial implementation of ActivityPub and of the Mastodon client API — - 📦 **Migration** — a page of its own behind your account: **Export** takes your profile, follows, followers, blocks, mutes, bookmarks, likes and every post you have written as a zip (your private key is deliberately not in it), **Import** reads one back — including an archive from `occ user:export` — and a third section brings your follows over from Mastodon, Pixelfed, GoToSocial or Akkoma via their `following_accounts.csv`. It says plainly which networks cannot be imported from, and leaves a whole-account `Move` to `occ social:account:alias` and `occ social:account:move`, since that federates and cannot be undone. - ✍️ **Composer** — write posts and replies, pick a visibility (public, unlisted, followers-only, direct), insert emoji, and attach images. `@mentions` and `#hashtags` typed by hand are extracted from the text and turned into real recipients and tags. - 📊 **Polls** — write one in the composer (up to four options, single or multiple choice, 30 minutes to a week), and view and vote on federated ones. Votes travel to the poll's author as ActivityPub vote notes, incoming votes are counted, and the new totals federate back as `Update{Question}` (`lib/Service/PollService.php`). -- 🖼️ **Media attachments** — images (JPEG, PNG, GIF, WebP, AVIF, and **HEIC/HEIF** from an iPhone, transcoded to JPEG on the way in where the server has Imagick with libheif), video (MP4, WebM, QuickTime) and audio (MP3, MP4/AAC, OGG/Opus, WAV, FLAC); `filterMimeTypes()` in `lib/Service/CacheDocumentService.php` is the exact list. Up to **ten** per post. Video and audio are stored as-is — **no transcoding** — but a video gets a **poster frame** where the server has ffmpeg, so a page of videos can be scrolled without downloading one, and `/media/{uuid}` answers **byte ranges**, so a video can actually be seeked. Video has a ceiling of its own (`max_video_size`, 2 GB) rather than the 10 MB `max_size` a picture is held to, because a video is streamed to storage a chunk at a time and never held in memory; the largest uploads go through `POST /api/v1/media/from-file`, where the Files app has already done the chunking. Clients upload through `POST /api/v2/media` (or v1), with alt text via `description`, editable with `PUT /api/v1/media/{id}` and attached to a status with `media_ids`. A **focal point** (`focus`, Mastodon's `x,y`) says where the subject is, so a square crop keeps it in frame; it federates as `focalPoint`. +- 🖼️ **Media attachments** — images (JPEG, PNG, GIF, WebP, AVIF, and **HEIC/HEIF** from an iPhone, transcoded to JPEG on the way in where the server has Imagick with libheif), video (MP4, WebM, QuickTime), audio (MP3, MP4/AAC, OGG/Opus, WAV, FLAC) — **and files**: PDF, plain text, Markdown, CSV, ZIP, EPUB and the ODF and Office document formats, shown as a named download card under the post and federated as an ActivityPub `Document` with their mime (Mastodon shows them as a link). Nothing a browser would run — no HTML, SVG or scripts — and the picture ceiling (`max_size`) applies; `filterMimeTypes()` in `lib/Service/CacheDocumentService.php` is the exact list. Up to **ten** per post. Video and audio are stored as-is — **no transcoding** — but a video gets a **poster frame** where the server has ffmpeg, so a page of videos can be scrolled without downloading one, and `/media/{uuid}` answers **byte ranges**, so a video can actually be seeked. Video has a ceiling of its own (`max_video_size`, 2 GB) rather than the 10 MB `max_size` a picture is held to, because a video is streamed to storage a chunk at a time and never held in memory; the largest uploads go through `POST /api/v1/media/from-file`, where the Files app has already done the chunking. Clients upload through `POST /api/v2/media` (or v1), with alt text via `description`, editable with `PUT /api/v1/media/{id}` and attached to a status with `media_ids`. A **focal point** (`focus`, Mastodon's `x,y`) says where the subject is, so a square crop keeps it in frame; it federates as `focalPoint`. - 🔒 **Metadata stripped on upload** — a photo off a phone carries an Exif block, and that block routinely carries the coordinates it was taken at. Every uploaded picture has its Exif, XMP, IPTC and comment blocks removed before it is stored or federated (`lib/Service/ImageMetadataService.php`). Done on the container rather than by re-encoding, so nothing loses a generation of quality, and the ICC colour profile is deliberately kept. A photo that says it is rotated is turned the right way up first, since the orientation tag goes with the rest. - 🎨 **Filters** — eight mild adjustments in the composer, previewed live and baked into the copy that is posted. - 🔳 **Profile grid** — a profile draws as a grid of squares or as a timeline, whichever you last chose. The crop follows each picture's focal point. @@ -72,7 +72,6 @@ It is a partial implementation of ActivityPub and of the Mastodon client API — These are absent from the code today, not merely rough edges: - **No status translation.** The `translate` action returns the post unchanged (`lib/Service/ActionService.php`). -- **No document or file attachments.** Images, video and audio only — anything else is refused by `filterMimeTypes()` (`lib/Service/CacheDocumentService.php`). - **No custom emoji of this instance's own.** Emoji from other servers render; `/api/v1/custom_emojis` returns an empty list (`lib/Controller/ApiController.php`, `customEmojis()`). - **No streaming API and no push subscriptions.** Third-party clients poll. (The web client does get live timelines when [notify_push](https://github.com/nextcloud/notify_push) is installed — that is a Nextcloud channel, not a Mastodon one.) diff --git a/docs/API.md b/docs/API.md index 2f318c8f6..f7245b6f8 100644 --- a/docs/API.md +++ b/docs/API.md @@ -490,7 +490,7 @@ remote one, as on Mastodon: both describe a user of *this* instance. | Method | Route | Auth | Parameters | Description | |--------|-------|------|------------|-------------| -| POST | `/api/v1/media` | public, no-csrf (viewer required, `write` scope) | Multipart: `file` (required, read from `$_FILES['file']`), `description` (the alt text) | Stores an upload and returns the `MediaAttachment` (images get a resized preview and blurhash; video/audio are stored as-is with the media itself as `preview_url`). Refuses a mime type outside `CacheDocumentService::filterMimeTypes()`, and a file over its ceiling with a 422. **Two ceilings**: `max_size` (10 MB by default) for everything, and `max_video_size` (2048 MB) for video, because an image is read whole into memory to be stripped and resized while a video is copied to storage a chunk at a time and never held. The request-time check can only go on the type the *client* declared, so it is applied again after the content is sniffed — a file that claimed to be a video to get past the first check is refused by the second. The stored row is **not** public: `public` is what lets the unauthenticated `/media/{uuid}` route serve the file, and it is only set later, when a post attaches the media and that post is `public` or `unlisted`. `focus` is accepted and not stored. Rate-limited per user. | +| POST | `/api/v1/media` | public, no-csrf (viewer required, `write` scope) | Multipart: `file` (required, read from `$_FILES['file']`), `description` (the alt text) | Stores an upload and returns the `MediaAttachment` (images get a resized preview and blurhash; video/audio are stored as-is with the media itself as `preview_url`). Refuses a mime type outside `CacheDocumentService::filterMimeTypes()` — images, video, audio and the document kinds in `DOCUMENT_MIME_TYPES` (PDF, text, Markdown, CSV, ZIP, EPUB, ODF, Office), which come back as `type: "unknown"` with no `preview_url` and, when no `description` was sent, the upload's file name as the description — and a file over its ceiling with a 422. **Two ceilings**: `max_size` (10 MB by default) for everything, and `max_video_size` (2048 MB) for video, because an image is read whole into memory to be stripped and resized while a video is copied to storage a chunk at a time and never held. The request-time check can only go on the type the *client* declared, so it is applied again after the content is sniffed — a file that claimed to be a video to get past the first check is refused by the second. The stored row is **not** public: `public` is what lets the unauthenticated `/media/{uuid}` route serve the file, and it is only set later, when a post attaches the media and that post is `public` or `unlisted`. `focus` is accepted and not stored. Rate-limited per user. | | POST | `/api/v2/media` | public, no-csrf (viewer required, `write` scope) | Same as v1 | The same upload; modern clients POST v2 and only fall back to v1 on a 404. | | POST | `/api/v1/media/from-file` | public, no-csrf (viewer required, `write` scope) | Body (JSON or form-encoded): `path` (required, relative to the viewer's own files), `description` (the alt text) | **Nextcloud extension, not a Mastodon route.** Attaches a file the viewer already has in Nextcloud, so a picture that is already on the server does not have to be downloaded and uploaded back. The path is resolved inside the viewer's own user folder and nowhere else — a share they can read is fair game, a traversal is a 422 `no such file`, and so is a folder. The bytes are copied, not referenced: a post keeps the picture it was published with, so moving or deleting the original later cannot empty a post that has already federated. Everything after that is the upload path — the same size ceiling, the same mime filter, the same resizing — and the answer is the same `MediaAttachment`, equally not public until a post says so. Rate-limited per user. | | GET | `/api/v1/media/{nid}` | public, no-csrf | `nid` (path), `preview` (default `''`, ignored) | One of the viewer's own attachments, by the id the upload returned. 404 for an unknown id or someone else's attachment. | diff --git a/docs/Architecture.md b/docs/Architecture.md index 105556a16..9729d1edf 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -7,7 +7,7 @@ Nextcloud Social is a federated social networking app built on the W3C ActivityP **App ID:** `social` **Namespace:** `OCA\Social` **License:** AGPL-3.0-or-later -**App version:** 0.19.51 +**App version:** 0.19.53 **Supported Nextcloud versions:** 35 – 36 **Supported PHP versions:** 8.3 – 8.5 diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index 6c26669c5..883650d6b 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -1145,7 +1145,8 @@ public function mediaNew(): DataResponse { $this->storeAttachment( $name, (string)$this->request->getParam('description', ''), - (string)$this->request->getParam('focus', '') + (string)$this->request->getParam('focus', ''), + basename($file['name'] ?? '') ), Http::STATUS_OK ); @@ -1208,7 +1209,7 @@ public function mediaFromFile(): DataResponse { fclose($handle); } - return new DataResponse($this->storeAttachment($tmpPath, $description), Http::STATUS_OK); + return new DataResponse($this->storeAttachment($tmpPath, $description, '', $file->getName()), Http::STATUS_OK); } catch (Throwable $e) { return $this->error($e); } @@ -1249,7 +1250,7 @@ private function ownFile(string $userId, string $path): File { * * @return MediaAttachment the entity a client is answered with */ - private function storeAttachment(string $tmpPath, string $description, string $focus = ''): MediaAttachment { + private function storeAttachment(string $tmpPath, string $description, string $focus = '', string $filename = ''): MediaAttachment { $document = new Document(); $document->setLocal(true); $document->setAccount($this->viewer->getPreferredUsername()); @@ -1273,6 +1274,12 @@ private function storeAttachment(string $tmpPath, string $description, string $f } $this->cacheDocumentService->saveFromTempToCache($document, $tmpPath); + if ($description === '' && $filename !== '' && CacheDocumentService::isDocumentMime($document->getMediaType())) { + // a file is known by its name, and the description is the one + // field the entity has for saying what it is; a picture with no + // alt text stays undescribed, which is a fact worth keeping + $document->setDescription(mb_substr($filename, 0, 255)); + } $service = AP::instance()->getInterfaceForItem($document); $service->save($document); diff --git a/lib/Model/ActivityPub/Object/Document.php b/lib/Model/ActivityPub/Object/Document.php index 1a73695c2..db056435b 100644 --- a/lib/Model/ActivityPub/Object/Document.php +++ b/lib/Model/ActivityPub/Object/Document.php @@ -512,7 +512,9 @@ public function convertToMediaAttachment( $mime = ''; if (strpos($this->getMediaType(), '/')) { [$type, $mime] = explode('/', $this->getMediaType(), 2); - $media->setType($type); + // Mastodon's four kinds, and its word for everything else: a PDF + // is not an "application", it is a file a client cannot render + $media->setType(in_array($type, ['image', 'video', 'audio', 'gifv'], true) ? $type : 'unknown'); } // the whole of it, not just the half the client entity shows: what @@ -530,7 +532,11 @@ public function convertToMediaAttachment( $media->setUrl($this->streamUrl($urlGenerator)); } else { $media->setUrl($this->getMediaUrl($urlGenerator, $mime)); - $media->setPreviewUrl($this->previewUrl($urlGenerator, $mime)); + // a file has no preview of itself; handing the file back as + // one would have a client try to draw a PDF as a picture + if ($media->getType() !== 'unknown') { + $media->setPreviewUrl($this->previewUrl($urlGenerator, $mime)); + } } } diff --git a/lib/Model/Client/MediaAttachment.php b/lib/Model/Client/MediaAttachment.php index f6b2ea160..c9f0b687c 100644 --- a/lib/Model/Client/MediaAttachment.php +++ b/lib/Model/Client/MediaAttachment.php @@ -242,6 +242,13 @@ public static function guessMediaType(string $type, string $url): string { 'mp4' => 'video/mp4', 'm4v' => 'video/mp4', 'webm' => 'video/webm', 'mov' => 'video/quicktime', 'mp3' => 'audio/mpeg', 'm4a' => 'audio/mp4', 'ogg' => 'audio/ogg', 'oga' => 'audio/ogg', 'opus' => 'audio/opus', 'wav' => 'audio/wav', 'flac' => 'audio/flac', 'aac' => 'audio/aac', + 'pdf' => 'application/pdf', 'txt' => 'text/plain', 'md' => 'text/markdown', 'csv' => 'text/csv', + 'zip' => 'application/zip', 'epub' => 'application/epub+zip', + 'odt' => 'application/vnd.oasis.opendocument.text', 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', ]; if (isset($byExtension[$extension])) { return $byExtension[$extension]; diff --git a/lib/Service/CacheDocumentService.php b/lib/Service/CacheDocumentService.php index eb0f75223..13ec1e270 100644 --- a/lib/Service/CacheDocumentService.php +++ b/lib/Service/CacheDocumentService.php @@ -452,13 +452,41 @@ public function filterMimeTypes(string $mime) { 'audio/aac', ]; - if (in_array($mime, $allowedMimeType)) { + if (in_array($mime, $allowedMimeType, true) || in_array($mime, self::DOCUMENT_MIME_TYPES, true)) { return; } throw new CacheContentMimeTypeException(); } + /** + * The files a post may carry that are not media: what people on a + * Nextcloud actually have to share. Stored as they are, served as + * downloads, shown as a file card; on the wire an ActivityPub `Document` + * with its mime, which Mastodon renders as an `unknown` attachment (a + * link) and other Nextclouds render as a file. Nothing a browser would + * execute: no HTML, no SVG, no scripts. + */ + public const DOCUMENT_MIME_TYPES = [ + 'application/pdf', + 'text/plain', + 'text/markdown', + 'text/csv', + 'application/zip', + 'application/epub+zip', + 'application/vnd.oasis.opendocument.text', + 'application/vnd.oasis.opendocument.spreadsheet', + 'application/vnd.oasis.opendocument.presentation', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + ]; + + /** Whether a mime is one of the file kinds above rather than a picture, a video or a sound. */ + public static function isDocumentMime(string $mime): bool { + return in_array($mime, self::DOCUMENT_MIME_TYPES, true); + } + /** * Builds the preview copy, and refuses to try on content that only claims * to be an image. diff --git a/lib/Service/InstanceService.php b/lib/Service/InstanceService.php index dece1b68b..45f397cd8 100644 --- a/lib/Service/InstanceService.php +++ b/lib/Service/InstanceService.php @@ -51,6 +51,13 @@ class InstanceService { 'video/mp4', 'video/webm', 'video/quicktime', 'video/ogg', 'video/x-matroska', 'audio/mpeg', 'audio/mp4', 'audio/ogg', 'audio/opus', 'audio/wav', 'audio/x-wav', 'audio/flac', 'audio/aac', 'audio/webm', 'audio/3gpp', + 'application/pdf', 'text/plain', 'text/markdown', 'text/csv', 'application/zip', 'application/epub+zip', + 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet', + 'application/vnd.oasis.opendocument.presentation', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'application/x-msdownload', 'text/html', ]; /** diff --git a/src/components/Composer/Composer.vue b/src/components/Composer/Composer.vue index 31063e0d9..98a7978cd 100644 --- a/src/components/Composer/Composer.vue +++ b/src/components/Composer/Composer.vue @@ -325,12 +325,33 @@ const MAX_LENGTH = 500 */ const ACCEPTED_MEDIA_TYPES = ['image/', 'video/', 'audio/'] +/** + * The files a post may carry besides media, as CacheDocumentService:: + * DOCUMENT_MIME_TYPES has them: what people on a Nextcloud actually have to + * share. The extensions are for a browser that reports no type for a file. + */ +const ACCEPTED_DOCUMENT_TYPES = [ + 'application/pdf', + 'text/plain', + 'text/markdown', + 'text/csv', + 'application/zip', + 'application/epub+zip', + 'application/vnd.oasis.opendocument.text', + 'application/vnd.oasis.opendocument.spreadsheet', + 'application/vnd.oasis.opendocument.presentation', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', +] +const ACCEPTED_DOCUMENT_EXTENSIONS = ['.pdf', '.txt', '.md', '.csv', '.zip', '.epub', '.odt', '.ods', '.odp', '.docx', '.xlsx', '.pptx'] + /** * What the file picker offers. Narrower than what the composer takes from a * drop or an upload: Files is where the pictures are, and an audio file picked * out of a folder tree is not what this button is for. */ -const PICKABLE_MEDIA_TYPES = ['image/*', 'video/*'] +const PICKABLE_MEDIA_TYPES = ['image/*', 'video/*', ...ACCEPTED_DOCUMENT_TYPES] /** what a post may carry, as Stream::MAX_ATTACHMENTS holds it server-side */ const MAX_ATTACHMENTS = 10 @@ -581,7 +602,7 @@ export default { ...mapStores(useTimelineStore), /** @return {string} the `accept` of the file dialog, from one list */ acceptedTypes() { - return ACCEPTED_MEDIA_TYPES.map((type) => `${type}*`).join(',') + return [...ACCEPTED_MEDIA_TYPES.map((type) => `${type}*`), ...ACCEPTED_DOCUMENT_TYPES, ...ACCEPTED_DOCUMENT_EXTENSIONS].join(',') }, /** @return {boolean} whether the composer holds a picture */ @@ -739,6 +760,8 @@ export default { * Another post's page, in the same component: the router reuses this * view, so without this the box would still be replying to the post * the reader has navigated away from. + * + * @param post */ inReplyTo(post) { this.replyTo = post @@ -1014,7 +1037,10 @@ export default { * @return {boolean} whether the file dialog would have offered it */ acceptsFile(file) { - return ACCEPTED_MEDIA_TYPES.some((type) => (file.type || '').startsWith(type)) + const type = file.type || '' + return ACCEPTED_MEDIA_TYPES.some((prefix) => type.startsWith(prefix)) + || ACCEPTED_DOCUMENT_TYPES.includes(type) + || ACCEPTED_DOCUMENT_EXTENSIONS.some((extension) => (file.name || '').toLowerCase().endsWith(extension)) }, /** @param {DragEvent} event a drag arriving over the card */ @@ -1178,7 +1204,7 @@ export default { // `@nextcloud/dialogs`, and it is wanted only by somebody who // has just clicked "attach from Files" const { getFilePickerBuilder } = await import('@nextcloud/dialogs') - picked = await getFilePickerBuilder(translate('social', 'Pick pictures to attach')) + picked = await getFilePickerBuilder(translate('social', 'Pick files to attach')) .setMultiSelect(true) .setMimeTypeFilter(PICKABLE_MEDIA_TYPES) .allowDirectories(false) diff --git a/src/components/MediaAttachment.vue b/src/components/MediaAttachment.vue index a9c369c0e..1edf0acc3 100644 --- a/src/components/MediaAttachment.vue +++ b/src/components/MediaAttachment.vue @@ -34,6 +34,22 @@ preload="metadata" @click="onMediaClick" @loadedmetadata="previewLoaded = true" /> + + + + + {{ fileName }} + {{ fileKind }} + + - + @@ -66,12 +82,14 @@ import { decode } from 'blurhash' import { translate } from '@nextcloud/l10n' import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon' +import FileDocumentOutline from 'vue-material-design-icons/FileDocumentOutline.vue' import ImageOff from 'vue-material-design-icons/ImageOff.vue' import logger from '../services/logger.js' export default { name: 'MediaAttachment', components: { + FileDocumentOutline, ImageOff, NcLoadingIcon, }, @@ -112,6 +130,31 @@ export default { return this.attachment?.type === 'video' || this.attachment?.type === 'audio' }, + /** @return {boolean} whether this is a file rather than something to draw or play */ + isFile() { + return this.attachment?.type === 'unknown' + }, + + /** + * @return {string} what the file is called: the description the server + * filled in from the upload's name, else the last bit of its address + */ + fileName() { + const description = (this.attachment?.description || '').trim() + if (description !== '') { + return description + } + const path = (this.attachment?.url || this.attachment?.remote_url || '').split('?')[0] + return decodeURIComponent(path.split('/').pop() || '') || t('social', 'File') + }, + + /** @return {string} the extension, upper-cased, as a kind label; '' when there is none */ + fileKind() { + const path = (this.attachment?.url || this.attachment?.remote_url || '').split('?')[0] + const match = /\.([a-z0-9]{1,8})$/i.exec(path) + return match ? match[1].toUpperCase() : '' + }, + /** * The still to show before anything is played. A federated video has * one -- PeerTube publishes it and this instance mirrors it -- and it @@ -250,6 +293,52 @@ export default { diff --git a/tests/Controller/ApiControllerTest.php b/tests/Controller/ApiControllerTest.php index 3ead3f587..931a5d8f1 100644 --- a/tests/Controller/ApiControllerTest.php +++ b/tests/Controller/ApiControllerTest.php @@ -3057,6 +3057,7 @@ private function userFolderHolding( $file = $this->createMock(File::class); $file->method('getSize')->willReturn(strlen($contents)); + $file->method('getName')->willReturn(basename($path)); $file->method('getPath')->willReturn('/alice/files/' . ltrim($path, '/')); $file->method('fopen')->willReturn($stream); diff --git a/tests/Model/ActivityPub/Object/DocumentTest.php b/tests/Model/ActivityPub/Object/DocumentTest.php index bf6944c94..698f389bb 100644 --- a/tests/Model/ActivityPub/Object/DocumentTest.php +++ b/tests/Model/ActivityPub/Object/DocumentTest.php @@ -162,6 +162,28 @@ public function testConvertToMediaAttachmentOfAVideoUsesTheMediaAsItsOwnPreview( $this->assertSame($media->getUrl(), $media->getPreviewUrl(), 'no resized copy: the media is the preview'); } + /** + * A PDF is not an "application": it is a file a client cannot render, which + * Mastodon calls `unknown` -- and it has no preview of itself, so none is + * offered rather than the file being handed back as a picture. + */ + public function testAFileConvertsToAnUnknownAttachmentWithoutAPreview(): void { + $document = new Document(); + $document->setNid(9) + ->setMediaType('application/pdf') + ->setLocalCopy('doc') + ->setResizedCopy('') + ->setDescription('minutes.pdf'); + + $media = $document->convertToMediaAttachment($this->urlGenerator); + + $this->assertSame('unknown', $media->getType()); + $this->assertSame('application/pdf', $media->getMediaType()); + $this->assertSame('https://cloud.example.org/social.Api.mediaOpen/doc.pdf', $media->getUrl()); + $this->assertSame('', $media->getPreviewUrl(), 'a file is not its own preview'); + $this->assertSame('minutes.pdf', $media->getDescription()); + } + public function testConvertToMediaAttachmentWithoutUrlGeneratorLeavesLocalUrlsEmpty(): void { $document = new Document(); $document->setMediaType('video/mp4') diff --git a/tests/Model/Client/MediaAttachmentTest.php b/tests/Model/Client/MediaAttachmentTest.php index 87141c5f6..d84dad989 100644 --- a/tests/Model/Client/MediaAttachmentTest.php +++ b/tests/Model/Client/MediaAttachmentTest.php @@ -122,6 +122,7 @@ public function testARowWithoutTheMimeGetsAGuessRatherThanNothing(): void { $this->assertSame('image/jpeg', MediaAttachment::guessMediaType('image', 'https://x.example/media/uuid')); $this->assertSame('video/mp4', MediaAttachment::guessMediaType('gifv', 'https://x.example/media/uuid')); $this->assertSame('audio/mpeg', MediaAttachment::guessMediaType('audio', '')); + $this->assertSame('application/pdf', MediaAttachment::guessMediaType('unknown', 'https://x.example/media/report.pdf')); $this->assertSame('', MediaAttachment::guessMediaType('unknown', '')); } diff --git a/tests/Service/CacheDocumentServiceTest.php b/tests/Service/CacheDocumentServiceTest.php index acadb7081..d7b613a59 100644 --- a/tests/Service/CacheDocumentServiceTest.php +++ b/tests/Service/CacheDocumentServiceTest.php @@ -208,6 +208,14 @@ public static function allowedMimeProvider(): array { 'opus' => ['audio/opus'], 'wav' => ['audio/wav'], 'flac' => ['audio/flac'], + // files: what people on a Nextcloud actually have to share + 'pdf' => ['application/pdf'], + 'plain text' => ['text/plain'], + 'markdown' => ['text/markdown'], + 'csv' => ['text/csv'], + 'zip' => ['application/zip'], + 'odt' => ['application/vnd.oasis.opendocument.text'], + 'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'], ]; } @@ -224,10 +232,19 @@ public static function rejectedMimeProvider(): array { 'html' => ['text/html'], 'php' => ['application/x-httpd-php'], 'mkv' => ['video/x-matroska'], + // a file a browser would run is not a file a post may carry + 'exe' => ['application/x-msdownload'], + 'javascript' => ['text/javascript'], 'empty' => [''], ]; } + public function testADocumentMimeIsKnownAsOne(): void { + $this->assertTrue(CacheDocumentService::isDocumentMime('application/pdf')); + $this->assertFalse(CacheDocumentService::isDocumentMime('image/png')); + $this->assertFalse(CacheDocumentService::isDocumentMime('text/html')); + } + #[DataProvider('rejectedMimeProvider')] public function testFilterMimeTypesRejectsEverythingElse(string $mime): void { $this->expectException(CacheContentMimeTypeException::class); @@ -523,9 +540,10 @@ public function testAVideoMayBeLargerThanAnImageMay(): void { $this->service->filterSize('video/mp4', 3000 * 1048576); } - public function testSaveFromTempToCacheRejectsNonImages(): void { + public function testSaveFromTempToCacheRejectsWhatABrowserWouldRun(): void { $tmp = tempnam(sys_get_temp_dir(), 'social-test-'); - file_put_contents($tmp, 'plain text'); + // plain text is a file a post may carry now; a web page is not + file_put_contents($tmp, "\nx"); try { $this->appData->expects($this->never())->method($this->anything()); diff --git a/tests/js/components/Composer.test.js b/tests/js/components/Composer.test.js index 2c9ae65b4..ef4f85ea5 100644 --- a/tests/js/components/Composer.test.js +++ b/tests/js/components/Composer.test.js @@ -709,7 +709,7 @@ describe('Composer', () => { await addFromFiles(wrapper) expect(builder.setMultiSelect).toHaveBeenCalledWith(true) - expect(builder.setMimeTypeFilter).toHaveBeenCalledWith(['image/*', 'video/*']) + expect(builder.setMimeTypeFilter).toHaveBeenCalledWith(expect.arrayContaining(['image/*', 'video/*', 'application/pdf', 'text/plain'])) expect(builder.allowDirectories).toHaveBeenCalledWith(false) expect(store.createMediaFromFile).toHaveBeenCalledWith({ path: beach }) }) @@ -993,7 +993,7 @@ describe('Composer', () => { it('refuses a file the file dialog would never have offered', async () => { const { wrapper, store } = mountComposer() - const notes = new File(['x'], 'notes.txt', { type: 'text/plain' }) + const notes = new File(['x'], 'setup.exe', { type: 'application/x-msdownload' }) await dispatch(wrapper, card(wrapper).element, dragEvent('drop', { dataTransfer: transfer([notes]) })) await flushPromises() @@ -1006,7 +1006,7 @@ describe('Composer', () => { it('takes the pictures out of a mixed drop and leaves the rest', async () => { const { wrapper, store } = mountComposer() const file = picture() - const notes = new File(['x'], 'notes.txt', { type: 'text/plain' }) + const notes = new File(['x'], 'setup.exe', { type: 'application/x-msdownload' }) await dispatch(wrapper, card(wrapper).element, dragEvent('drop', { dataTransfer: transfer([file, notes]) })) await flushPromises() @@ -1057,7 +1057,7 @@ describe('Composer', () => { it('leaves a pasted file of a kind it cannot take to the input', async () => { const { wrapper, store } = mountComposer() - const notes = new File(['x'], 'notes.txt', { type: 'text/plain' }) + const notes = new File(['x'], 'setup.exe', { type: 'application/x-msdownload' }) const event = await dispatch(wrapper, input(wrapper).element, pasteEvent([notes])) await flushPromises() diff --git a/tests/js/components/MediaAttachment.test.js b/tests/js/components/MediaAttachment.test.js index 778bd609c..0ce12f9c1 100644 --- a/tests/js/components/MediaAttachment.test.js +++ b/tests/js/components/MediaAttachment.test.js @@ -84,6 +84,31 @@ describe('MediaAttachment', () => { expect(video.attributes('preload')).toBe('metadata') }) + it('shows a file as a named download card rather than a broken picture', () => { + const wrapper = mount(MediaAttachment, { + props: { attachment: { id: '9', type: 'unknown', url: 'https://cloud.example.org/media/abc.pdf', preview_url: null, description: 'minutes.pdf' }, interactive: true }, + }) + + const card = wrapper.find('a.attachment__file') + expect(card.exists()).toBe(true) + expect(card.attributes('href')).toBe('https://cloud.example.org/media/abc.pdf') + expect(card.attributes('download')).toBeDefined() + expect(card.find('.attachment__file-name').text()).toBe('minutes.pdf') + expect(card.find('.attachment__file-kind').text()).toBe('PDF') + // nothing to load, so nothing spins and no placeholder says it failed + expect(wrapper.find('.attachment__failed').exists()).toBe(false) + expect(wrapper.findComponent({ name: 'NcLoadingIcon' }).exists()).toBe(false) + }) + + it('names an undescribed file after the last part of its address', () => { + const wrapper = mount(MediaAttachment, { + props: { attachment: { id: '9', type: 'unknown', url: 'https://cloud.example.org/media/Q3%20report.docx?x=1', preview_url: null, description: null } }, + }) + + expect(wrapper.find('.attachment__file-name').text()).toBe('Q3 report.docx') + expect(wrapper.find('.attachment__file-kind').text()).toBe('DOCX') + }) + it('renders the small preview image', () => { const wrapper = mount(MediaAttachment, { props: { attachment } }) expect(wrapper.find('img.attachment__preview').attributes('src')).toBe(attachment.preview_url) diff --git a/tests/js/components/PostAttachment.test.js b/tests/js/components/PostAttachment.test.js index 12041d0a0..5eb22fbd4 100644 --- a/tests/js/components/PostAttachment.test.js +++ b/tests/js/components/PostAttachment.test.js @@ -79,6 +79,26 @@ describe('PostAttachment', () => { getContext.mockRestore() }) + it('lists files under the pictures instead of tiling them', () => { + const picture = attachments(1)[0] + const file = { id: 'f1', type: 'unknown', url: 'https://cloud.example.org/media/notes.pdf', preview_url: null, description: 'notes.pdf' } + const wrapper = mountAttachments([picture, file]) + + // one tile, for the picture; the file is not in the tile count + expect(wrapper.findAll('button.attachment')).toHaveLength(1) + const files = wrapper.find('.post-attachments__files') + expect(files.exists()).toBe(true) + expect(files.findAllComponents(MediaAttachment).map((tile) => tile.props('attachment'))).toEqual([file]) + }) + + it('shows a post that carries only a file as a file, with no empty gallery', () => { + const file = { id: 'f1', type: 'unknown', url: 'https://cloud.example.org/media/notes.pdf', preview_url: null, description: 'notes.pdf' } + const wrapper = mountAttachments([file], { mediaFirst: true }) + + expect(wrapper.find('.gallery-mosaic').exists()).toBe(false) + expect(wrapper.find('.post-attachments__files').exists()).toBe(true) + }) + it('renders a tile per attachment for up to four attachments', () => { const items = attachments(4) const wrapper = mountAttachments(items) From 456fb82bd4fa1446bacb5e886babff618ffb52e3 Mon Sep 17 00:00:00 2001 From: Frank Karlitschek Date: Mon, 14 Sep 2026 11:56:36 +0200 Subject: [PATCH 2/2] 0.19.53, rebuilt bundle Signed-off-by: Frank Karlitschek Co-Authored-By: Claude Fable 5.1 --- appinfo/info.xml | 2 +- composer.json | 2 +- js/social-adminAnnouncements.js | 4 ++-- js/social-composer.js | 4 ++-- js/social-dashboard.js | 4 ++-- ...sign-icons_ArrowUp_vue-src_views_TimelineSinglePost_vue.js | 4 ++-- ...aterial-design-icons_ArrowUp_vue-src_views_Timeline_vue.js | 4 ++-- js/social-oauth.js | 4 ++-- js/social-ostatus.js | 4 ++-- js/social-profile.js | 4 ++-- js/social-profilePage.js | 4 ++-- js/social-social.js | 4 ++-- js/social-src_components_Search_vue.js | 4 ++-- js/social-src_components_TimelineEntry_vue.js | 4 ++-- ...s-src_components_MediaAttachment_vue-src_compone-71be4a.js | 4 ++-- ...odules_he_he_js-node_modules_tributejs_dist_trib-a2d0d9.js | 3 +++ ...js-node_modules_tributejs_dist_trib-a2d0d9.js.LICENSE.txt} | 0 ...nents_NcActionButton_index_mjs-node_modules_next-5a196b.js | 3 --- ...nents_NcActionButton_index_mjs-node_modules_next-662cde.js | 2 ++ ...nents_NcActionButton_index_mjs-node_modules_next-9572c9.js | 2 -- ...nents_NcActionLink_index_mjs-node_modules_nextcl-c91063.js | 2 ++ ...extcloud_vue_dist_components_NcDateTimePicker_index_mjs.js | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 24 files changed, 40 insertions(+), 38 deletions(-) create mode 100644 js/social-vendors-node_modules_debounce_index_js-node_modules_he_he_js-node_modules_tributejs_dist_trib-a2d0d9.js rename js/{social-vendors-node_modules_nextcloud_vue_dist_components_NcActionButton_index_mjs-node_modules_next-5a196b.js.LICENSE.txt => social-vendors-node_modules_debounce_index_js-node_modules_he_he_js-node_modules_tributejs_dist_trib-a2d0d9.js.LICENSE.txt} (100%) delete mode 100644 js/social-vendors-node_modules_nextcloud_vue_dist_components_NcActionButton_index_mjs-node_modules_next-5a196b.js create mode 100644 js/social-vendors-node_modules_nextcloud_vue_dist_components_NcActionButton_index_mjs-node_modules_next-662cde.js delete mode 100644 js/social-vendors-node_modules_nextcloud_vue_dist_components_NcActionButton_index_mjs-node_modules_next-9572c9.js create mode 100644 js/social-vendors-node_modules_nextcloud_vue_dist_components_NcActionLink_index_mjs-node_modules_nextcl-c91063.js diff --git a/appinfo/info.xml b/appinfo/info.xml index 3fad909b4..feeb6146c 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -32,7 +32,7 @@ What it does: - 📱 A layout for phones: the post takes the width of the screen, the face inside the card, the composer's toolbar wrapping You can pin your own posts to the top of your profile, bookmark any post, and see which hashtags the instance is using most. This is a partial implementation of ActivityPub and of the Mastodon client API. Blocking, muting and reporting (with a moderation panel in the administration settings) are supported, as are locked accounts with approvable follow requests. Polls are fully supported: create your own, and view and vote on federated ones. Profiles carry an avatar, a banner image and up to four profile metadata fields. Posts that link somewhere get a link preview card. Image (JPEG, PNG, GIF, WebP, AVIF, and HEIC from an iPhone where the server can read it), video (MP4, WebM, QuickTime) and audio attachments are supported, up to ten per post, each with alt text and a focal point. It does not offer lists.]]> - 0.19.51 + 0.19.53 agpl Benedikt Schächner Social diff --git a/composer.json b/composer.json index f1f29d4f7..63762b7f2 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "nextcloud/social", "description": "Social app", "license": "AGPL-3.0-or-later", - "version": "0.19.51", + "version": "0.19.53", "minimum-stability": "stable", "authors": [ { diff --git a/js/social-adminAnnouncements.js b/js/social-adminAnnouncements.js index 03d7d16b7..1e4242540 100644 --- a/js/social-adminAnnouncements.js +++ b/js/social-adminAnnouncements.js @@ -1,2 +1,2 @@ -(()=>{"use strict";var e={37844(e,d,o){var _=o(47606),n=o(55705);async function s(e){const{showError:d}=await Promise.all([o.e("vendors-node_modules_mdi_js_mdi_js-node_modules_nextcloud_vue_dist_chunks_NcDialogButton_vue_-bd7251"),o.e("vendors-node_modules_nextcloud_capabilities_dist_index_mjs-node_modules_nextcloud_l10n_dist_g-2544a4"),o.e("vendors-node_modules_nextcloud_dialogs_dist_index_mjs-node_modules_nextcloud_vue_dist_chunks_-6b9069")]).then(()=>o(84973));d(e)}async function a(e,d,o){const s=await fetch((0,n.Jv)("/apps/social/admin/announcements")+d,{method:e,headers:{"Content-Type":"application/json",requesttoken:(0,_.do)()},body:void 0===o?void 0:JSON.stringify(o)}),a=await s.json().catch(()=>({}));if(!s.ok)throw new Error(a.error||"request failed: "+s.status);return a}function l(e,d){return e?d?e.slice(0,10):e.slice(0,16).replace("T"," "):"—"}function c(e){const d=document.getElementById("social-announcements-list");if(null!==d){if(d.textContent="",0===e.length){const e=d.insertRow().insertCell();return e.colSpan=5,void(e.textContent=t("social","No announcements."))}e.forEach(e=>{const o=d.insertRow();o.dataset.announcementId=e.id,o.insertCell().textContent=e.text,o.insertCell().textContent=l(e.starts_at,e.all_day),o.insertCell().textContent=l(e.ends_at,e.all_day),o.insertCell().textContent=e.active?t("social","Shown now"):t("social","Not shown");const _=document.createElement("button");_.type="button",_.className="social-announcement-remove",_.textContent=t("social","Remove"),o.insertCell().appendChild(_)})}}async function f(){const e=document.getElementById("social-announcement-text"),d=document.getElementById("social-announcement-starts"),o=document.getElementById("social-announcement-ends"),_=document.getElementById("social-announcement-all-day");if(""!==e.value.trim())try{const n=await a("POST","",{text:e.value,starts_at:d.value,ends_at:o.value,all_day:_.checked});e.value="",d.value="",o.value="",_.checked=!1,c(n.announcements)}catch(e){await s(t("social","Could not post the announcement")+": "+e.message)}}async function u(e){const d=e.target.closest(".social-announcement-remove");if(null===d)return;const o=d.closest("tr");if(window.confirm(t("social","Remove this announcement? Everybody stops seeing it.")))try{c((await a("DELETE","/"+o.dataset.announcementId)).announcements)}catch{await s(t("social","Could not remove the announcement"))}}function i(){return null!==document.getElementById("social-announcements")&&(document.getElementById("social-announcement-add").addEventListener("click",f),document.getElementById("social-announcements-list").addEventListener("click",u),async function(){try{c((await a("GET","")).announcements)}catch{await s(t("social","Could not read the announcements"))}}(),!0)}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",i):i()}};const d={};function o(_){const n=d[_];if(void 0!==n)return n.exports;const s=d[_]={id:_,exports:{}};return e[_].call(s.exports,s,s.exports,o),s.exports}o.m=e,(()=>{const e=[];o.O=(d,_,n,s)=>{if(_){s=s||0;for(var a=e.length;a>0&&e[a-1][2]>s;a--)e[a]=e[a-1];return void(e[a]=[_,n,s])}let l=1/0;for(a=0;a=s)||!Object.keys(o.O).every(e=>o.O[e](_[t]))?(c=!1,s{const d=e&&e.__esModule?()=>e.default:()=>e;return o.d(d,{a:d}),d},o.d=(e,d)=>{if(Array.isArray(d))for(var _=0;_Promise.all(Object.keys(o.f).reduce((d,_)=>(o.f[_](e,d),d),[])),o.u=e=>"social-"+e+".js?v="+{"vendors-node_modules_mdi_js_mdi_js-node_modules_nextcloud_vue_dist_chunks_NcDialogButton_vue_-bd7251":"3e2b71cf51197c794c29","vendors-node_modules_nextcloud_capabilities_dist_index_mjs-node_modules_nextcloud_l10n_dist_g-2544a4":"a28894d8e9152722e7d9","vendors-node_modules_nextcloud_dialogs_dist_index_mjs-node_modules_nextcloud_vue_dist_chunks_-6b9069":"2ae851b0670e53454f64","vendors-node_modules_nextcloud_vue_dist_chunks_NcCheckboxRadioSwitch_mjs":"c4909cd77df1a40a4330","vendors-node_modules_nextcloud_files_dist_index_mjs":"53b15fa0c41f0d8a097e","vendors-node_modules_nextcloud_dialogs_dist_chunks_preview_mjs-node_modules_nextcloud_vue_dis-5d78a6":"945c5d0bcfe0eaf4fa9c","vendors-node_modules_nextcloud_dialogs_dist_chunks_ConflictPicker_mjs":"af68252184eed3d59710","vendors-node_modules_nextcloud_vue_dist_components_NcTextField_index_mjs":"3ea6155dab1c4c354348","vendors-node_modules_nextcloud_dialogs_dist_chunks_FilePicker_mjs":"4b0c621cb1fd75fbe5f0",node_modules_nextcloud_dialogs_dist_chunks_PublicAuthPrompt_mjs:"caeda7e73e2e577aa10a","vendors-node_modules_nextcloud_vue_dist_chunks_NcColorPicker_mjs":"fc2fcf460bd3bde759a2","node_modules_nextcloud_vue_dist_chunks_colors_mjs-node_modules_nextcloud_vue_dist_components_-f3952b":"a4dd6dcd265410f1c309","vendors-node_modules_nextcloud_vue_dist_components_NcDateTimePicker_index_mjs":"180e863b386b008b611b","node_modules_date-fns_locale_af_js":"568c99a50eb22ae04840","node_modules_date-fns_locale_ar-DZ_js":"6e89d6a2d4cfcfa30c61","node_modules_date-fns_locale_ar-EG_js":"be1ff514b0d6fc1198a0","node_modules_date-fns_locale_ar-MA_js":"f8c4b791ee7ce50afe69","node_modules_date-fns_locale_ar-SA_js":"98906aed5ac7d6f9c3e7","node_modules_date-fns_locale_ar-TN_js":"37139facab7983a31fe7","node_modules_date-fns_locale_ar_js":"1e6a3bd7f34370443280","node_modules_date-fns_locale_az_js":"b347187cd3a657be3303","vendors-node_modules_date-fns_locale_be-tarask_js":"6288131657b45948cd72","vendors-node_modules_date-fns_locale_be_js":"a036a6a873e65e6d996a","node_modules_date-fns_locale_bg_js":"3716dd6c67895e0a97d8","node_modules_date-fns_locale_bn_js":"ad9fd51273200f70045d","node_modules_date-fns_locale_bs_js":"5be34af0dfd14657caf7","node_modules_date-fns_locale_ca_js":"ecb9b5b9392c29a6b9c2","node_modules_date-fns_locale_ckb_js":"ab52b99a3de68d8bb3c8","node_modules_date-fns_locale_cs_js":"f3e801b3926b6291d052","node_modules_date-fns_locale_cy_js":"a843834a72287ae2eace","node_modules_date-fns_locale_da_js":"2c5b0383c0b5e043e0ad","node_modules_date-fns_locale_de-AT_js":"e72bc9a15ff2f0d4105e","node_modules_date-fns_locale_de_js":"75fd92a90b4980d5edbc","node_modules_date-fns_locale_el_js":"7c6edc1d0f74fc216589","node_modules_date-fns_locale_en-AU_js":"5ca04682bd5c803b05ea","node_modules_date-fns_locale_en-CA_js":"81a7afedca2c47e140fc","node_modules_date-fns_locale_en-GB_js":"98fddc07e3795d5874ed","node_modules_date-fns_locale_en-IE_js":"361c2f65d743136e3ec4","node_modules_date-fns_locale_en-IN_js":"82e2b1c92852a8d6e55a","node_modules_date-fns_locale_en-NZ_js":"59aad90c55be04d643bd","node_modules_date-fns_locale_en-ZA_js":"d30ac50ccb916d26ea79","node_modules_date-fns_locale_eo_js":"47a4dc5a2679155ca540","node_modules_date-fns_locale_es_js":"95976019b5a76437ad94","node_modules_date-fns_locale_et_js":"69f31ec6a4f1134c0da8","node_modules_date-fns_locale_eu_js":"943453591aba185b79f3","node_modules_date-fns_locale_fa-IR_js":"d5a6b40b3bd909abeaeb","node_modules_date-fns_locale_fi_js":"761ba05d1ef3c085f7fb","node_modules_date-fns_locale_fr-CA_js":"391a41fbf81e8532e012","node_modules_date-fns_locale_fr-CH_js":"06da66fe72d7fefe7a8a","node_modules_date-fns_locale_fr_js":"da0057b66ba6c20796e5","node_modules_date-fns_locale_fy_js":"f65f659e599327620007","node_modules_date-fns_locale_gd_js":"39ede8a94802f1a3cda8","node_modules_date-fns_locale_gl_js":"2f2ac87357e14fb26633","node_modules_date-fns_locale_gu_js":"179ac12c398d68c43610","node_modules_date-fns_locale_he_js":"a28a70115acac7dad4a9","node_modules_date-fns_locale_hi_js":"bd7e847b67a3cc859b76","node_modules_date-fns_locale_hr_js":"6dfaa62c0b98e880c941","node_modules_date-fns_locale_ht_js":"a95dd7bceabba303dda0","node_modules_date-fns_locale_hu_js":"ac8392ad5b1678ebfd98","node_modules_date-fns_locale_hy_js":"3a171c3d3a191690280b","node_modules_date-fns_locale_id_js":"66977128d3cef88a1e9b","node_modules_date-fns_locale_is_js":"44c1169ff3cba87f99b6","node_modules_date-fns_locale_it-CH_js":"e0795c631760ce2725c1","node_modules_date-fns_locale_it_js":"4e0a835b7a27689064e8","node_modules_date-fns_locale_ja-Hira_js":"92a285c1b2f3697da366","node_modules_date-fns_locale_ja_js":"a53163ec60f6a76248c6","node_modules_date-fns_locale_ka_js":"f4422b6464a934a7bf45","vendors-node_modules_date-fns_locale_kk_js":"8951e01f07eab56f7ee6","node_modules_date-fns_locale_km_js":"c9b96b942d36af319913","node_modules_date-fns_locale_kn_js":"99ec5cabd1585ad21a6a","node_modules_date-fns_locale_ko_js":"802f82cd669c91c0640f","node_modules_date-fns_locale_lb_js":"32aa5e189a8e07a0c21c","node_modules_date-fns_locale_lt_js":"e1e38226c66fac38b0b8","node_modules_date-fns_locale_lv_js":"9cf0f6e0b553d722381d","node_modules_date-fns_locale_mk_js":"925bd061c887a4760da4","node_modules_date-fns_locale_mn_js":"5b8012f4a110b6f0a3d2","node_modules_date-fns_locale_ms_js":"5049aa6f858d8647a525","node_modules_date-fns_locale_mt_js":"0f004de3b66ece204a61","node_modules_date-fns_locale_nb_js":"3cb82fc909bac3133c43","node_modules_date-fns_locale_nl-BE_js":"81f72650f040c3b48200","node_modules_date-fns_locale_nl_js":"ed3b052a5967d8d30726","node_modules_date-fns_locale_nn_js":"f1c96e02ed6bd0cce945","node_modules_date-fns_locale_oc_js":"efce8c0a2c7116e87565","node_modules_date-fns_locale_pl_js":"8eac758ad7ba41178d57","node_modules_date-fns_locale_pt-BR_js":"d8aa7ea1913c80e7e883","node_modules_date-fns_locale_pt_js":"e6d012791c238caf6da9","node_modules_date-fns_locale_ro_js":"38984a9e22e4375f4ad5","vendors-node_modules_date-fns_locale_ru_js":"66e37716f2be9f613c49","node_modules_date-fns_locale_se_js":"e9ce2b63e8962a8f53f3","node_modules_date-fns_locale_sk_js":"2eee76a3773ef54960db","node_modules_date-fns_locale_sl_js":"ab249dbe99080ab1dc49","node_modules_date-fns_locale_sq_js":"710e55d8844bf0b34b0f","node_modules_date-fns_locale_sr-Latn_js":"85dc36f5095f0a216480","node_modules_date-fns_locale_sr_js":"1de8a167bdfaf7b39e7d","node_modules_date-fns_locale_sv_js":"130bbf67a52d352e04d8","node_modules_date-fns_locale_ta_js":"b0fa7943fbff7d2afb62","node_modules_date-fns_locale_te_js":"047cade7b1eda27b7675","node_modules_date-fns_locale_th_js":"e6530c0aaf2f2d480ee3","node_modules_date-fns_locale_tr_js":"de92f93650bdf6dd30a6","node_modules_date-fns_locale_ug_js":"0a8f60c94518dca1f291","vendors-node_modules_date-fns_locale_uk_js":"8b92ef8649d20c2101d5","node_modules_date-fns_locale_uz-Cyrl_js":"e0dc86eededd804e89ee","node_modules_date-fns_locale_uz_js":"0c31e869dc60b8e46c99","node_modules_date-fns_locale_vi_js":"193ee6a45cf99fca1efb","node_modules_date-fns_locale_zh-CN_js":"ed2b186b169669aafacc","node_modules_date-fns_locale_zh-HK_js":"1bfa9f02dab15b463cd8","node_modules_date-fns_locale_zh-TW_js":"5b37dba10ab292c20b95"}[e],o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,d)=>Object.prototype.hasOwnProperty.call(e,d),(()=>{const e={},d="social:";o.l=(_,n,s,a)=>{if(e[_])return void e[_].push(n);let l,t;if(void 0!==s){const e=document.getElementsByTagName("script");for(var c=0;c{l.onerror=l.onload=null,clearTimeout(u);const n=e[_];if(delete e[_],l.parentNode?.removeChild(l),n?.forEach(e=>e(o)),d)return d(o)},u=setTimeout(f.bind(null,void 0,{type:"timeout",target:l}),12e4);l.onerror=f.bind(null,l.onerror),l.onload=f.bind(null,l.onload),t&&document.head.appendChild(l)}})(),o.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.j="adminAnnouncements",o.dn=e=>{var d=Object.getOwnPropertyDescriptor(e,"name");(!d||!d.writable&&d.configurable)&&Object.defineProperty(e,"name",{value:"default",configurable:!0})},o.p="/apps/social/js/",(()=>{const e={adminAnnouncements:0};o.f.j=(d,_)=>{let n=o.o(e,d)?e[d]:void 0;if(0!==n)if(n)_.push(n[2]);else{const s=new Promise((o,_)=>n=e[d]=[o,_]);_.push(n[2]=s);const a=new Error,l=_=>{if(o.o(e,d)&&(n=e[d],0!==n&&(e[d]=void 0),n)){const e=_&&("load"===_.type?"missing":_.type),o=_&&_.target&&_.target.src;a.message="Loading chunk "+d+" failed.\n("+e+": "+o+")",a.name="ChunkLoadError",a.type=e,a.request=o,a.event=_,n[1](a)}};o.l(o.p+o.u(d),l,"chunk-"+d,d)}},o.O.j=d=>0===e[d];const d=(d,_)=>{let[n,s,a]=_;var l,t,c=0;if(n.some(d=>0!==e[d])){for(l in s)o.o(s,l)&&(o.m[l]=s[l]);if(a)var f=a(o)}for(d&&d(_);co(37844));_=o.O(_)})(); -//# sourceMappingURL=social-adminAnnouncements.js.map?v=49c4e0c8b40687af2e00 \ No newline at end of file +(()=>{"use strict";var e={37844(e,d,o){var _=o(47606),n=o(55705);async function s(e){const{showError:d}=await Promise.all([o.e("vendors-node_modules_mdi_js_mdi_js-node_modules_nextcloud_vue_dist_chunks_NcDialogButton_vue_-bd7251"),o.e("vendors-node_modules_nextcloud_capabilities_dist_index_mjs-node_modules_nextcloud_l10n_dist_g-2544a4"),o.e("vendors-node_modules_nextcloud_dialogs_dist_index_mjs-node_modules_nextcloud_vue_dist_chunks_-6b9069")]).then(()=>o(84973));d(e)}async function a(e,d,o){const s=await fetch((0,n.Jv)("/apps/social/admin/announcements")+d,{method:e,headers:{"Content-Type":"application/json",requesttoken:(0,_.do)()},body:void 0===o?void 0:JSON.stringify(o)}),a=await s.json().catch(()=>({}));if(!s.ok)throw new Error(a.error||"request failed: "+s.status);return a}function l(e,d){return e?d?e.slice(0,10):e.slice(0,16).replace("T"," "):"—"}function c(e){const d=document.getElementById("social-announcements-list");if(null!==d){if(d.textContent="",0===e.length){const e=d.insertRow().insertCell();return e.colSpan=5,void(e.textContent=t("social","No announcements."))}e.forEach(e=>{const o=d.insertRow();o.dataset.announcementId=e.id,o.insertCell().textContent=e.text,o.insertCell().textContent=l(e.starts_at,e.all_day),o.insertCell().textContent=l(e.ends_at,e.all_day),o.insertCell().textContent=e.active?t("social","Shown now"):t("social","Not shown");const _=document.createElement("button");_.type="button",_.className="social-announcement-remove",_.textContent=t("social","Remove"),o.insertCell().appendChild(_)})}}async function f(){const e=document.getElementById("social-announcement-text"),d=document.getElementById("social-announcement-starts"),o=document.getElementById("social-announcement-ends"),_=document.getElementById("social-announcement-all-day");if(""!==e.value.trim())try{const n=await a("POST","",{text:e.value,starts_at:d.value,ends_at:o.value,all_day:_.checked});e.value="",d.value="",o.value="",_.checked=!1,c(n.announcements)}catch(e){await s(t("social","Could not post the announcement")+": "+e.message)}}async function u(e){const d=e.target.closest(".social-announcement-remove");if(null===d)return;const o=d.closest("tr");if(window.confirm(t("social","Remove this announcement? Everybody stops seeing it.")))try{c((await a("DELETE","/"+o.dataset.announcementId)).announcements)}catch{await s(t("social","Could not remove the announcement"))}}function i(){return null!==document.getElementById("social-announcements")&&(document.getElementById("social-announcement-add").addEventListener("click",f),document.getElementById("social-announcements-list").addEventListener("click",u),async function(){try{c((await a("GET","")).announcements)}catch{await s(t("social","Could not read the announcements"))}}(),!0)}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",i):i()}};const d={};function o(_){const n=d[_];if(void 0!==n)return n.exports;const s=d[_]={id:_,exports:{}};return e[_].call(s.exports,s,s.exports,o),s.exports}o.m=e,(()=>{const e=[];o.O=(d,_,n,s)=>{if(_){s=s||0;for(var a=e.length;a>0&&e[a-1][2]>s;a--)e[a]=e[a-1];return void(e[a]=[_,n,s])}let l=1/0;for(a=0;a=s)||!Object.keys(o.O).every(e=>o.O[e](_[t]))?(c=!1,s{const d=e&&e.__esModule?()=>e.default:()=>e;return o.d(d,{a:d}),d},o.d=(e,d)=>{if(Array.isArray(d))for(var _=0;_Promise.all(Object.keys(o.f).reduce((d,_)=>(o.f[_](e,d),d),[])),o.u=e=>"social-"+e+".js?v="+{"vendors-node_modules_mdi_js_mdi_js-node_modules_nextcloud_vue_dist_chunks_NcDialogButton_vue_-bd7251":"3e2b71cf51197c794c29","vendors-node_modules_nextcloud_capabilities_dist_index_mjs-node_modules_nextcloud_l10n_dist_g-2544a4":"a28894d8e9152722e7d9","vendors-node_modules_nextcloud_dialogs_dist_index_mjs-node_modules_nextcloud_vue_dist_chunks_-6b9069":"2ae851b0670e53454f64","vendors-node_modules_nextcloud_vue_dist_chunks_NcCheckboxRadioSwitch_mjs":"c4909cd77df1a40a4330","vendors-node_modules_nextcloud_files_dist_index_mjs":"53b15fa0c41f0d8a097e","vendors-node_modules_nextcloud_dialogs_dist_chunks_preview_mjs-node_modules_nextcloud_vue_dis-5d78a6":"945c5d0bcfe0eaf4fa9c","vendors-node_modules_nextcloud_dialogs_dist_chunks_ConflictPicker_mjs":"af68252184eed3d59710","vendors-node_modules_nextcloud_vue_dist_components_NcTextField_index_mjs":"3ea6155dab1c4c354348","vendors-node_modules_nextcloud_dialogs_dist_chunks_FilePicker_mjs":"4b0c621cb1fd75fbe5f0",node_modules_nextcloud_dialogs_dist_chunks_PublicAuthPrompt_mjs:"caeda7e73e2e577aa10a","vendors-node_modules_nextcloud_vue_dist_chunks_NcColorPicker_mjs":"fc2fcf460bd3bde759a2","node_modules_nextcloud_vue_dist_chunks_colors_mjs-node_modules_nextcloud_vue_dist_components_-f3952b":"a4dd6dcd265410f1c309","vendors-node_modules_nextcloud_vue_dist_components_NcDateTimePicker_index_mjs":"9230b70e5e8a07f09e58","node_modules_date-fns_locale_af_js":"568c99a50eb22ae04840","node_modules_date-fns_locale_ar-DZ_js":"6e89d6a2d4cfcfa30c61","node_modules_date-fns_locale_ar-EG_js":"be1ff514b0d6fc1198a0","node_modules_date-fns_locale_ar-MA_js":"f8c4b791ee7ce50afe69","node_modules_date-fns_locale_ar-SA_js":"98906aed5ac7d6f9c3e7","node_modules_date-fns_locale_ar-TN_js":"37139facab7983a31fe7","node_modules_date-fns_locale_ar_js":"1e6a3bd7f34370443280","node_modules_date-fns_locale_az_js":"b347187cd3a657be3303","vendors-node_modules_date-fns_locale_be-tarask_js":"6288131657b45948cd72","vendors-node_modules_date-fns_locale_be_js":"a036a6a873e65e6d996a","node_modules_date-fns_locale_bg_js":"3716dd6c67895e0a97d8","node_modules_date-fns_locale_bn_js":"ad9fd51273200f70045d","node_modules_date-fns_locale_bs_js":"5be34af0dfd14657caf7","node_modules_date-fns_locale_ca_js":"ecb9b5b9392c29a6b9c2","node_modules_date-fns_locale_ckb_js":"ab52b99a3de68d8bb3c8","node_modules_date-fns_locale_cs_js":"f3e801b3926b6291d052","node_modules_date-fns_locale_cy_js":"a843834a72287ae2eace","node_modules_date-fns_locale_da_js":"2c5b0383c0b5e043e0ad","node_modules_date-fns_locale_de-AT_js":"e72bc9a15ff2f0d4105e","node_modules_date-fns_locale_de_js":"75fd92a90b4980d5edbc","node_modules_date-fns_locale_el_js":"7c6edc1d0f74fc216589","node_modules_date-fns_locale_en-AU_js":"5ca04682bd5c803b05ea","node_modules_date-fns_locale_en-CA_js":"81a7afedca2c47e140fc","node_modules_date-fns_locale_en-GB_js":"98fddc07e3795d5874ed","node_modules_date-fns_locale_en-IE_js":"361c2f65d743136e3ec4","node_modules_date-fns_locale_en-IN_js":"82e2b1c92852a8d6e55a","node_modules_date-fns_locale_en-NZ_js":"59aad90c55be04d643bd","node_modules_date-fns_locale_en-ZA_js":"d30ac50ccb916d26ea79","node_modules_date-fns_locale_eo_js":"47a4dc5a2679155ca540","node_modules_date-fns_locale_es_js":"95976019b5a76437ad94","node_modules_date-fns_locale_et_js":"69f31ec6a4f1134c0da8","node_modules_date-fns_locale_eu_js":"943453591aba185b79f3","node_modules_date-fns_locale_fa-IR_js":"d5a6b40b3bd909abeaeb","node_modules_date-fns_locale_fi_js":"761ba05d1ef3c085f7fb","node_modules_date-fns_locale_fr-CA_js":"391a41fbf81e8532e012","node_modules_date-fns_locale_fr-CH_js":"06da66fe72d7fefe7a8a","node_modules_date-fns_locale_fr_js":"da0057b66ba6c20796e5","node_modules_date-fns_locale_fy_js":"f65f659e599327620007","node_modules_date-fns_locale_gd_js":"39ede8a94802f1a3cda8","node_modules_date-fns_locale_gl_js":"2f2ac87357e14fb26633","node_modules_date-fns_locale_gu_js":"179ac12c398d68c43610","node_modules_date-fns_locale_he_js":"a28a70115acac7dad4a9","node_modules_date-fns_locale_hi_js":"bd7e847b67a3cc859b76","node_modules_date-fns_locale_hr_js":"6dfaa62c0b98e880c941","node_modules_date-fns_locale_ht_js":"a95dd7bceabba303dda0","node_modules_date-fns_locale_hu_js":"ac8392ad5b1678ebfd98","node_modules_date-fns_locale_hy_js":"3a171c3d3a191690280b","node_modules_date-fns_locale_id_js":"66977128d3cef88a1e9b","node_modules_date-fns_locale_is_js":"44c1169ff3cba87f99b6","node_modules_date-fns_locale_it-CH_js":"e0795c631760ce2725c1","node_modules_date-fns_locale_it_js":"4e0a835b7a27689064e8","node_modules_date-fns_locale_ja-Hira_js":"92a285c1b2f3697da366","node_modules_date-fns_locale_ja_js":"a53163ec60f6a76248c6","node_modules_date-fns_locale_ka_js":"f4422b6464a934a7bf45","vendors-node_modules_date-fns_locale_kk_js":"8951e01f07eab56f7ee6","node_modules_date-fns_locale_km_js":"c9b96b942d36af319913","node_modules_date-fns_locale_kn_js":"99ec5cabd1585ad21a6a","node_modules_date-fns_locale_ko_js":"802f82cd669c91c0640f","node_modules_date-fns_locale_lb_js":"32aa5e189a8e07a0c21c","node_modules_date-fns_locale_lt_js":"e1e38226c66fac38b0b8","node_modules_date-fns_locale_lv_js":"9cf0f6e0b553d722381d","node_modules_date-fns_locale_mk_js":"925bd061c887a4760da4","node_modules_date-fns_locale_mn_js":"5b8012f4a110b6f0a3d2","node_modules_date-fns_locale_ms_js":"5049aa6f858d8647a525","node_modules_date-fns_locale_mt_js":"0f004de3b66ece204a61","node_modules_date-fns_locale_nb_js":"3cb82fc909bac3133c43","node_modules_date-fns_locale_nl-BE_js":"81f72650f040c3b48200","node_modules_date-fns_locale_nl_js":"ed3b052a5967d8d30726","node_modules_date-fns_locale_nn_js":"f1c96e02ed6bd0cce945","node_modules_date-fns_locale_oc_js":"efce8c0a2c7116e87565","node_modules_date-fns_locale_pl_js":"8eac758ad7ba41178d57","node_modules_date-fns_locale_pt-BR_js":"d8aa7ea1913c80e7e883","node_modules_date-fns_locale_pt_js":"e6d012791c238caf6da9","node_modules_date-fns_locale_ro_js":"38984a9e22e4375f4ad5","vendors-node_modules_date-fns_locale_ru_js":"66e37716f2be9f613c49","node_modules_date-fns_locale_se_js":"e9ce2b63e8962a8f53f3","node_modules_date-fns_locale_sk_js":"2eee76a3773ef54960db","node_modules_date-fns_locale_sl_js":"ab249dbe99080ab1dc49","node_modules_date-fns_locale_sq_js":"710e55d8844bf0b34b0f","node_modules_date-fns_locale_sr-Latn_js":"85dc36f5095f0a216480","node_modules_date-fns_locale_sr_js":"1de8a167bdfaf7b39e7d","node_modules_date-fns_locale_sv_js":"130bbf67a52d352e04d8","node_modules_date-fns_locale_ta_js":"b0fa7943fbff7d2afb62","node_modules_date-fns_locale_te_js":"047cade7b1eda27b7675","node_modules_date-fns_locale_th_js":"e6530c0aaf2f2d480ee3","node_modules_date-fns_locale_tr_js":"de92f93650bdf6dd30a6","node_modules_date-fns_locale_ug_js":"0a8f60c94518dca1f291","vendors-node_modules_date-fns_locale_uk_js":"8b92ef8649d20c2101d5","node_modules_date-fns_locale_uz-Cyrl_js":"e0dc86eededd804e89ee","node_modules_date-fns_locale_uz_js":"0c31e869dc60b8e46c99","node_modules_date-fns_locale_vi_js":"193ee6a45cf99fca1efb","node_modules_date-fns_locale_zh-CN_js":"ed2b186b169669aafacc","node_modules_date-fns_locale_zh-HK_js":"1bfa9f02dab15b463cd8","node_modules_date-fns_locale_zh-TW_js":"5b37dba10ab292c20b95"}[e],o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,d)=>Object.prototype.hasOwnProperty.call(e,d),(()=>{const e={},d="social:";o.l=(_,n,s,a)=>{if(e[_])return void e[_].push(n);let l,t;if(void 0!==s){const e=document.getElementsByTagName("script");for(var c=0;c{l.onerror=l.onload=null,clearTimeout(u);const n=e[_];if(delete e[_],l.parentNode?.removeChild(l),n?.forEach(e=>e(o)),d)return d(o)},u=setTimeout(f.bind(null,void 0,{type:"timeout",target:l}),12e4);l.onerror=f.bind(null,l.onerror),l.onload=f.bind(null,l.onload),t&&document.head.appendChild(l)}})(),o.r=e=>{Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.j="adminAnnouncements",o.dn=e=>{var d=Object.getOwnPropertyDescriptor(e,"name");(!d||!d.writable&&d.configurable)&&Object.defineProperty(e,"name",{value:"default",configurable:!0})},o.p="/apps/social/js/",(()=>{const e={adminAnnouncements:0};o.f.j=(d,_)=>{let n=o.o(e,d)?e[d]:void 0;if(0!==n)if(n)_.push(n[2]);else{const s=new Promise((o,_)=>n=e[d]=[o,_]);_.push(n[2]=s);const a=new Error,l=_=>{if(o.o(e,d)&&(n=e[d],0!==n&&(e[d]=void 0),n)){const e=_&&("load"===_.type?"missing":_.type),o=_&&_.target&&_.target.src;a.message="Loading chunk "+d+" failed.\n("+e+": "+o+")",a.name="ChunkLoadError",a.type=e,a.request=o,a.event=_,n[1](a)}};o.l(o.p+o.u(d),l,"chunk-"+d,d)}},o.O.j=d=>0===e[d];const d=(d,_)=>{let[n,s,a]=_;var l,t,c=0;if(n.some(d=>0!==e[d])){for(l in s)o.o(s,l)&&(o.m[l]=s[l]);if(a)var f=a(o)}for(d&&d(_);co(37844));_=o.O(_)})(); +//# sourceMappingURL=social-adminAnnouncements.js.map?v=3c81cd5732d2f6d47d9d \ No newline at end of file diff --git a/js/social-composer.js b/js/social-composer.js index 9a64c1cf9..289213984 100644 --- a/js/social-composer.js +++ b/js/social-composer.js @@ -1,2 +1,2 @@ -"use strict";(self.webpackChunksocial=self.webpackChunksocial||[]).push([["composer"],{70204(t,e,i){var n=i(24877),o=i(75783),r=i(50772),a=i(8435),s=i(17334),l=i.n(s),A=i(7880),c=i(23447),d=i(65924),p=i(78209),u=i(99318),C=i(20641),m=i(71085),h=i(66981),g=i(66067),b=i.n(g),v=i(16865),f=i(70642),x=i(59201),y=i(55705),w=i(28908),k=i(27274),_=i(81958),B=i(70330),I=i(10712),D=i(25897),T=i.n(D),F=i(44894),P=i(63360),E=i(38161),L=i(96231),z=i(49124),S=i(9699),O=i(92069),N=i(59512);const $=["image/","video/","audio/"],W=["image/*","video/*"];let j=null;const M=()=>j??=Promise.all([i.e("emoji-picker"),i.e("vendors-node_modules_nextcloud_vue_dist_chunks_NcColorPicker_mjs"),i.e("vendors-node_modules_nextcloud_vue_dist_components_NcEmojiPicker_index_mjs")]).then(()=>i(62823)),X={name:"Composer",components:{NcAvatar:A.A,NcEmojiPicker:(0,C.$V)({loader:M,onError:t=>P.A.error("Could not load the emoji picker",{error:t})}),NcButton:c.A,NcLoadingIcon:d.A,ActorAvatar:x.A,Paperclip:a.A,EmoticonOutline:n.A,Close:o.A,FolderImage:r.A,AlertOutline:p.A,PollIcon:u.A,PreviewGrid:w.A,VisibilitySelect:k.A,SubmitStatusButton:B.A,MessageContent:I.Ay},directives:{FocusOnCreate:v.A},props:{initialMention:{type:Object,default:null},defaultVisibility:{type:String,default:void 0},startExpanded:{type:Boolean,default:!1},inReplyTo:{type:Object,default:null},initialPaths:{type:Array,default:()=>[]}},emits:["posted"],setup(){const{hostname:t}=(0,N.H)(),{currentUser:e}=(0,O.i)();return{hostname:t,currentUser:e}},data(){return{emojiPickerLoaded:!1,emojiPickerLoading:!1,statusContent:"",statusText:"",openedByHand:this.startExpanded,visibility:this.defaultVisibility||this.inReplyTo?.visibility||G()||"followers",loading:!1,uploading:!1,uploadProgress:0,progressLabel:"",picking:!1,pickCount:0,filterTimers:{},draggingFiles:!1,refusedDrop:!1,attachments:{},showPoll:!1,showWarning:!1,spoilerText:"",pollOptions:["",""],pollMultiple:!1,pollExpiresIn:86400,search:"",replyTo:this.inReplyTo,quoteOf:null,tributeOptions:{spaceSelectsMatch:!0,collection:[{trigger:"@",lookup:t=>t.key+t.value,menuItemTemplate:t=>'
'+t.original.key+'
",selectTemplate:t=>`@${t.original.value} `,values:l()(async(t,e)=>{t.length<1&&e([]);const i=(await this.remoteSearchAccounts(t)).data.result.accounts.map(t=>({key:t.preferredUsername,value:t.account,url:t.url,avatar:t.local?(0,y.Jv)(`/avatar/${t.preferredUsername}/32`):(0,y.Jv)(`apps/social/api/v1/global/actor/avatar?id=${t.id}`)}));P.A.debug("Found accounts for a mention",{count:i.length}),e(i)},200)},{trigger:"#",menuItemTemplate:t=>t.original.value,selectTemplate(t){let e;return e=void 0===t?this.currentMentionTextSnapshot:t.original.value,'#'+e+""},values:l()(async(t,e)=>{t.length<1&&e([]);const i=await this.remoteSearchHashtags(t),n=[...i.data.result.exact&&!Array.isArray(i.data.result.exact)?[{key:i.data.result.exact,value:i.data.result.exact}]:[],...i.data.result.tags.map(({hashtag:t})=>({key:t,value:t}))];P.A.debug("Found hashtags for a mention",{count:n.length}),e(n)},200)}],noMatchTemplate(){if("#"===this.current.collection.trigger)return""===this.current.mentionText?void 0:'
  • #'+this.current.mentionText+"
  • "}}}},computed:{...(0,L.n2)(z.A),acceptedTypes:()=>$.map(t=>`${t}*`).join(","),hasAttachments(){return Object.keys(this.attachments).length>0},attachmentsFull(){return Object.keys(this.attachments).length>=10},prompt(){return this.hasAttachments?(0,m.Tl)("social","Write a caption…"):null!==this.replyTo?(0,m.Tl)("social","Write a reply…"):(0,m.Tl)("social","What would you like to share?")},anchoredReply(){return null!==this.inReplyTo&&this.replyTo?.id===this.inReplyTo.id},undescribed(){return Object.values(this.attachments).filter(t=>void 0!==t.data?.id&&""===(t.description||"").trim()).length},failedUploads(){return Object.values(this.attachments).filter(t=>!0===t.failed).length},hasPendingUploads(){return Object.values(this.attachments).some(t=>!0!==t.failed&&null===t.data)},mediaIds(){return Object.values(this.attachments).map(t=>t.data?.id).filter(t=>null!=t)},undescribedWarning(){return(0,m.zw)("social","%n attachment has no description","%n attachments have no description",this.undescribed)},charactersLeftLabel(){return this.statusIsTooLong?(0,m.zw)("social","%n character too many","%n characters too many",-this.charsLeft):(0,m.zw)("social","%n character left","%n characters left",this.charsLeft)},canPost(){return!this.hasPendingUploads&&(!this.statusIsTooLong&&(!this.statusIsEmpty&&!("direct"===this.visibility&&!this.hasMentions)))},statusIsEmpty(){return 0===this.statusText.trim().length&&0===this.mediaIds.length},expanded(){return this.openedByHand||this.loading||null!==this.replyTo&&!this.anchoredReply||null!==this.quoteOf||this.showPoll||this.showWarning||!this.statusIsEmpty||Object.keys(this.attachments).length>0},statusIsTooLong(){return this.statusText.length>500},charProgress(){return Math.min(this.statusText.length/500,1)},charsLeft(){return 500-this.statusText.length},hasMentions(){return/(?:^|\s)@[a-zA-Z0-9_.-]+/i.test(this.statusText)}},watch:{inReplyTo(t){this.replyTo=t},spoilerText:"rememberDraft",showWarning:"rememberDraft",visibility:"rememberDraft"},mounted(){this.tribute=new(T())(this.tributeOptions),this.tributeTarget=this.$refs.composerInput,this.tribute.attach(this.tributeTarget),this.onComposerReply=t=>{this.replyTo=t,this.prefillMessageWithMention(t.account),this.visibility=t.visibility,this.openedByHand=!0},F.A.on("composer-reply",this.onComposerReply),this.onComposerQuote=t=>{this.quoteOf=t},F.A.on("composer-quote",this.onComposerQuote),this.onComposerFocus=()=>this.focusInput(),F.A.on("shortcut:compose",this.onComposerFocus),this.restoreDraft(),null!==this.initialMention&&this.prefillMessageWithMention(this.initialMention);const t=this.initialPaths.filter(t=>"string"==typeof t&&""!==t&&"/"!==t);t.length>0&&(this.expand(),this.attachPaths(t)),this.onOutsideInteraction=t=>this.collapseIfIdle(t),document.addEventListener("pointerdown",this.onOutsideInteraction),document.addEventListener("focusin",this.onOutsideInteraction)},unmounted(){window.clearTimeout(this.refusalTimer),document.removeEventListener("pointerdown",this.onOutsideInteraction),document.removeEventListener("focusin",this.onOutsideInteraction),this.tribute&&this.tributeTarget&&this.tribute.detach(this.tributeTarget),F.A.off("composer-reply",this.onComposerReply),F.A.off("composer-quote",this.onComposerQuote),F.A.off("shortcut:compose",this.onComposerFocus)},methods:{expand(){this.openedByHand=!0},async loadEmojiPicker(){this.emojiPickerLoading||(this.emojiPickerLoading=!0,await M().catch(()=>{}),this.emojiPickerLoading=!1,this.emojiPickerLoaded=!0,await this.$nextTick(),await new Promise(t=>window.requestAnimationFrame(t)),this.$refs.emojiButton?.$el?.click())},collapseIfIdle(t){if(!this.openedByHand)return;const e=t.target;e instanceof Node&&!this.$el.contains(e)&&(e instanceof Element&&null!==e.closest(".v-popper__popper, .modal-mask")||(this.openedByHand=!1))},focusInput(){const t=this.$refs.composerInput;void 0!==t&&(t.focus(),"function"==typeof t.scrollIntoView&&t.scrollIntoView({block:"nearest"}))},prefillMessageWithMention(t){if(!this.statusIsEmpty||void 0===this.$refs.composerInput)return;let e=t.acct;e.includes("@")||(e+=`@${this.hostname}`);const i=document.createElement("span");i.className="mention",i.contentEditable="false";const n=document.createElement("a");n.href=t.url,n.target="_blank";const o=document.createElement("img");o.src=t.avatar,n.append(o,document.createTextNode(`@${e}`)),i.append(n),this.$refs.composerInput.replaceChildren(i,document.createTextNode(" ")),this.updateStatusContent()},updateStatusContent(){this.statusContent=this.$refs.composerInput.innerHTML,this.statusText=this.plainText(),this.rememberDraft()},plainText(){const t=this.$refs.composerInput;if(null==t)return"";const e=t.cloneNode(!0);return Array.from(e.getElementsByClassName("emoji")).forEach(t=>{t.replaceWith(document.createTextNode(t.getAttribute("alt")??""))}),b().decode(U(e).trim())},rememberDraft(){(0,E.J1)({text:this.statusText,spoilerText:this.showWarning?this.spoilerText:"",visibility:this.visibility})},restoreDraft(){const t=(0,E.C_)();return null!==t&&void 0!==this.$refs.composerInput&&(""!==t.text&&(this.$refs.composerInput.innerText=t.text),""!==t.spoilerText&&(this.showWarning=!0,this.spoilerText=t.spoilerText),(0,_.S)(t.visibility)&&void 0===this.defaultVisibility&&(this.visibility=t.visibility),this.updateStatusContent(),!0)},clickImportInput(){this.$refs.fileUploadInput.click()},async handleFileChange(t){const e=t.target,i=Array.from(e.files);e.value="",await this.attachFiles(i)},carriesFiles:t=>Array.from(t.dataTransfer?.types??[]).includes("Files"),acceptsFile:t=>$.some(e=>(t.type||"").startsWith(e)),handleDragEnter(t){this.carriesFiles(t)&&(t.preventDefault(),this.draggingFiles=!0)},handleDragOver(t){this.carriesFiles(t)&&(t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy"),this.draggingFiles=!0)},handleDragLeave(t){if(!this.draggingFiles)return;const e=t.relatedTarget;e instanceof Node&&this.$el.contains(e)||(this.draggingFiles=!1)},async handleDrop(t){this.carriesFiles(t)&&(t.preventDefault(),this.draggingFiles=!1,this.expand(),await this.attachDropped(Array.from(t.dataTransfer?.files??[])))},handlePaste(t){const e=Array.from(t.clipboardData?.files??[]).filter(t=>this.acceptsFile(t));0!==e.length&&(t.preventDefault(),this.attachFiles(e))},async attachDropped(t){const e=t.filter(t=>this.acceptsFile(t));e.length{this.refusedDrop=!1},400)},roomFor(t){const e=Math.max(10-Object.keys(this.attachments).length,0);return t.length>e&&this.announceCeiling(),t.slice(0,e)},announceCeiling(){(0,h.Qg)((0,m.zw)("social","A post can carry %n attachment","A post can carry %n attachments",10))},async pickFromFiles(){if(this.attachmentsFull)return void this.announceCeiling();let t;this.picking=!0;try{const{getFilePickerBuilder:e}=await Promise.all([i.e("vendors-node_modules_mdi_js_mdi_js-node_modules_nextcloud_vue_dist_chunks_NcDialogButton_vue_-bd7251"),i.e("vendors-node_modules_nextcloud_dialogs_dist_index_mjs")]).then(()=>i(84973));t=await e((0,m.Tl)("social","Pick pictures to attach")).setMultiSelect(!0).setMimeTypeFilter(W).allowDirectories(!1).build().pick()}catch(t){return void P.A.debug("The file picker was closed",{error:t})}finally{this.picking=!1}const e=(Array.isArray(t)?t:[t]).filter(t=>"string"==typeof t&&""!==t&&"/"!==t);0!==e.length&&(this.expand(),await this.attachPaths(e))},async attachPaths(t){const e=this.roomFor(t);this.picking=e.length>0,this.progressLabel=(0,m.Tl)("social","Attaching from Files…");for(const[t,i]of e.entries()){const n=`nextcloud:${++this.pickCount}:${i}`;this.attachments={...this.attachments,[n]:{file:null,path:i,data:null,failed:!1}},this.uploading=!0,this.uploadProgress=t/e.length;const o=await this.timelineStore.createMediaFromFile({path:i});this.uploadProgress=(t+1)/e.length,void 0!==this.attachments[n]&&(this.attachments={...this.attachments,[n]:{...this.attachments[n],data:void 0===o?.id?null:o,failed:void 0===o?.id}})}this.uploading=!1,this.uploadProgress=0,this.progressLabel="",this.picking=!1},applyFilter({key:t,filter:e}){const i=this.attachments[t];void 0!==i&&(this.attachments={...this.attachments,[t]:{...i,filter:e}},window.clearTimeout(this.filterTimers[t]),this.filterTimers[t]=window.setTimeout(()=>{this.reuploadFiltered(t)},600))},async reuploadFiltered(t){const e=this.attachments[t];if(void 0===e?.file)return;const i=await(0,S.Yo)(e.file,e.filter||"none");if(void 0===this.attachments[t])return;const n=await this.timelineStore.createMedia({file:i});if(void 0===this.attachments[t])return;if(void 0===n?.id)return void P.A.warn("Could not upload the filtered copy; keeping the original");const o=(e.description||"").trim();""!==o&&this.timelineStore.describeMedia({id:n.id,description:o}),this.attachments={...this.attachments,[t]:{...this.attachments[t],data:n,failed:!1}}},async attachFiles(t){const e=this.roomFor(t);this.progressLabel=(0,m.Tl)("social","Uploading…");for(const[t,i]of e.entries()){const n=URL.createObjectURL(i);this.attachments={...this.attachments,[n]:{file:i,data:null,failed:!1}},this.uploading=!0,this.uploadProgress=t/e.length;const o=await this.timelineStore.createMedia({file:i,onProgress:i=>{this.uploadProgress=(t+i)/e.length}});this.uploading=!1,this.uploadProgress=0,void 0!==this.attachments[n]&&(this.attachments={...this.attachments,[n]:{...this.attachments[n],data:void 0===o?.id?null:o,failed:void 0===o?.id}})}this.progressLabel=""},insert(t){if("object"==typeof t){const e=t[Object.keys(t)[0]];t=e[Object.keys(e)[0]]}const e=this.$refs.composerInput.lastChild,i=document.createElement("div");if(i.textContent=t+" ",null===e)this.$refs.composerInput.innerHTML=i.innerHTML;else switch(e.tagName){case"BR":e.before(i.firstChild);break;case"DIV":if("BR"===e.lastChild.tagName)e.lastChild.before(i.firstChild);else e.append(i.firstChild);break;default:e.after(i.firstChild)}this.updateStatusContent()},keyup(t){t.ctrlKey&&this.createPost()},updatePostFromTribute(){this.updateStatusContent()},n:m.zw,async createPost(){if(!this.canPost||this.loading)return;const t=this.plainText(),e=this.showWarning?this.spoilerText.trim():"",i={content_type:"",media_ids:this.mediaIds,sensitive:""!==e,spoiler_text:e,status:t,in_reply_to_id:this.replyTo?.id,quote_id:this.quoteOf?.id,visibility:this.visibility},n=this.pollOptions.map(t=>t.trim()).filter(t=>""!==t);let o;this.showPoll&&n.length>=2&&(i.poll={options:n,expires_in:this.pollExpiresIn,multiple:this.pollMultiple}),P.A.debug("Posting status",{visibility:i.visibility,attachments:i.media_ids.length});try{this.loading=!0,await this.saveDescriptions(),o=await this.timelineStore.post(i)}finally{this.loading=!1}void 0!==o?(this.replyTo=this.inReplyTo,this.quoteOf=null,null!==this.inReplyTo&&(this.openedByHand=this.startExpanded),this.$refs.composerInput.innerText="",Object.keys(this.attachments).forEach(t=>this.releasePreview(t)),this.attachments={},this.showPoll=!1,this.pollOptions=["",""],this.pollMultiple=!1,this.showWarning=!1,this.spoilerText="",(0,E.nr)(),this.updateStatusContent(),this.timelineStore.refreshTimeline(),this.$emit("posted"),F.A.emit("post-published",o)):this.rememberDraft()},toggleWarning(){this.showWarning=!this.showWarning,this.showWarning||(this.spoilerText="")},togglePoll(){this.showPoll=!this.showPoll,this.showPoll||(this.pollOptions=["",""],this.pollMultiple=!1)},closeReply(){this.replyTo=this.inReplyTo,null!==this.inReplyTo?this.openedByHand=this.startExpanded:this.timelineStore.setComposerDisplayStatus(!1)},removeQuote(){this.quoteOf=null},remoteSearchAccounts:t=>f.Ay.get((0,y.Jv)("apps/social/api/v1/global/accounts/search"),{params:{search:t}}),remoteSearchHashtags:t=>f.Ay.get((0,y.Jv)("apps/social/api/v1/global/tags/search"),{params:{search:t}}),deletePreview(t){const e={...this.attachments};delete e[t],this.attachments=e,this.releasePreview(t)},releasePreview(t){if(t.startsWith("blob:"))try{URL.revokeObjectURL(t)}catch(t){P.A.debug("Could not release a preview URL",{error:t})}},async saveDescriptions(){const t=Object.values(this.attachments).filter(t=>t.data?.id&&""!==(t.description||"").trim()&&(t.description||"").trim()!==t.saved);await Promise.all(t.map(t=>this.timelineStore.describeMedia({id:t.data.id,description:t.description.trim()})))},async commitDescription({key:t,description:e}){const i=this.attachments[t],n=(e||"").trim();void 0!==i?.data?.id&&""!==n&&n!==i.saved&&(this.attachments={...this.attachments,[t]:{...i,description:e,saved:n}},await this.timelineStore.describeMedia({id:i.data.id,description:n}))},describeAttachment({key:t,description:e}){void 0!==this.attachments[t]&&(this.attachments={...this.attachments,[t]:{...this.attachments[t],description:e}})}}};function G(){let t;try{t=window.localStorage.getItem("social.lastPostType")??""}catch{return""}return(0,_.S)(t)?t:""}function U(t){let e="";for(const i of Array.from(t.childNodes)){if(i.nodeType===Node.TEXT_NODE){e+=i.textContent||"";continue}if(i.nodeType!==Node.ELEMENT_NODE)continue;const t=i;"BR"!==t.tagName?(e+=U(t),["DIV","P","LI","BLOCKQUOTE","PRE"].includes(t.tagName)&&(e+="\n")):e+="\n"}return e}i.d(e,["A",0,X])},6766(t,e,i){var n=i(9699),o=i(71085);const r={name:"FilterPicker",props:{modelValue:{type:String,default:"none"},preview:{type:String,default:""}},emits:["update:modelValue"],computed:{filters:()=>(0,n.Tw)()},methods:{t:o.t}};i.d(e,["A",0,r])},58104(t,e,i){var n=i(70937),o=i(88864),r=i(71085);const a={name:"PreviewGrid",components:{PreviewGridItem:n.A,FileUpload:o.A},props:{uploadProgress:{type:Number,required:!0},progressLabel:{type:String,default:""},uploading:{type:Boolean,required:!0},miniatures:{type:Object,required:!0}},emits:["deleted","describe","commitDescription","filter"],computed:{count(){return Object.keys(this.miniatures).length},label(){return this.progressLabel||(0,r.Tl)("social","Uploading…")}},methods:{deletePreview(t){this.$emit("deleted",t)},t:r.Tl}};i.d(e,["A",0,a])},19013(t,e,i){var n=i(75783),o=i(89948),r=i(23447),a=i(9699),s=i(71085),l=i(17438);const A={name:"PreviewGridItem",components:{Close:n.A,FilterPicker:o.A,NcButton:r.A,MediaAttachment:l.A},props:{preview:{type:Object,required:!0},randomKey:{type:String,required:!0}},emits:["delete","describe","commitDescription"],computed:{described(){return""!==(this.preview.description||"").trim()},fieldId(){return"composer-alt-"+this.randomKey},isPicture(){const t=this.preview?.file?.type||this.preview?.data?.type||"";return t.startsWith("image/")?!["image/gif","image/webp"].includes(t):"image"===t},previewUrl(){return this.randomKey}},methods:{t:s.Tl,filterCss:a.M8}};i.d(e,["A",0,A])},61384(e,i,n){var o=n(37867);const r={name:"SubmitStatusButton",components:{NcButton:n(23447).A,Send:o.A},props:{visibility:{type:String,required:!0},disabled:{type:Boolean,default:!0}},emits:["click"],computed:{postTo(){switch(this.visibility){case"public":case"unlisted":return t("social","Post");case"followers":return t("social","Post to followers");case"direct":return t("social","Send message to mentioned users")}return""},currentVisibilityPostLabel(){return this.visibilityPostLabel(this.visibility)},visibilityPostLabel(){return e=>{switch(void 0===e&&(e=this.visibility),e){case"public":return t("social","Post publicly");case"followers":return t("social","Post to followers");case"direct":return t("social","Post to recipients");case"unlisted":return t("social","Post unlisted")}}}},methods:{handleClick(){this.$emit("click")}}};n.d(i,["A",0,r])},22188(t,e,i){var n=i(8018),o=i(99045),r=i(71085),a=i(81958),s=i(56589),l=i(63360);const A={name:"VisibilitySelect",components:{NcActions:n.A,NcActionButton:o.A,VisibilityIcon:s.A},props:{visibility:{type:String,required:!0}},emits:["update:visibility"],data:()=>({visibilitiesInfo:a.A}),computed:{selectedVisibilityDetails(){return a.A.find(({id:t})=>this.visibility===t)??a.A.find(({id:t})=>"followers"===t)}},methods:{switchType(t){this.$emit("update:visibility",t.id);try{localStorage.setItem("social.lastPostType",t.id)}catch(t){l.A.debug("Could not remember the visibility",{error:t})}},t:r.Tl}};i.d(e,["A",0,A])},2682(t,e,i){i.d(e,{X:()=>L});var n=i(20641),o=i(53751),r=i(90033);const a={key:0,class:"new-post__drop-hint","aria-hidden":"true"},s=["accept"],l={class:"new-post-author"},A={class:"post-author"},c={class:"post-author-name"},d={key:1,class:"reply-to"},p={class:"reply-info"},u={key:2,class:"quote-of"},C={class:"quote-info"},m=["aria-label","placeholder"],h=["contenteditable","aria-label","aria-describedby","placeholder"],g={key:1,class:"poll-editor"},b=["onUpdate:modelValue","placeholder"],v={class:"poll-editor__settings"},f=["aria-label"],x={value:1800},y={value:3600},w={value:21600},k={value:86400},_={value:259200},B={value:604800},I={class:"options"},D={class:"new-post-form__emoji-picker"},T={key:0,class:"composer-alt-warning",role:"status"},F=["title"],P={key:0,class:"char-ring__count","aria-hidden":"true"},E={class:"hidden-visually",role:"status"};function L(t,e,i,L,z,S){const O=(0,n.g2)("NcAvatar"),N=(0,n.g2)("ActorAvatar"),$=(0,n.g2)("Close"),W=(0,n.g2)("NcButton"),j=(0,n.g2)("MessageContent"),M=(0,n.g2)("PreviewGrid"),X=(0,n.g2)("Paperclip"),G=(0,n.g2)("FolderImage"),U=(0,n.g2)("AlertOutline"),q=(0,n.g2)("PollIcon"),V=(0,n.g2)("EmoticonOutline"),Y=(0,n.g2)("NcEmojiPicker"),R=(0,n.g2)("NcLoadingIcon"),Q=(0,n.g2)("VisibilitySelect"),H=(0,n.g2)("SubmitStatusButton");return(0,n.uX)(),(0,n.CE)("div",{class:(0,r.C4)(["new-post",{"new-post--collapsed":!S.expanded,"new-post--drop-target":z.draggingFiles,"new-post--refused":z.refusedDrop}]),"data-id":"",onFocusin:e[11]||(e[11]=(...t)=>S.expand&&S.expand(...t)),onDragenter:e[12]||(e[12]=(...t)=>S.handleDragEnter&&S.handleDragEnter(...t)),onDragover:e[13]||(e[13]=(...t)=>S.handleDragOver&&S.handleDragOver(...t)),onDragleave:e[14]||(e[14]=(...t)=>S.handleDragLeave&&S.handleDragLeave(...t)),onDrop:e[15]||(e[15]=(...t)=>S.handleDrop&&S.handleDrop(...t))},[z.draggingFiles?((0,n.uX)(),(0,n.CE)("div",a,[(0,n.Lk)("span",null,(0,r.v_)(t.t("social","Drop to attach")),1)])):(0,n.Q3)("",!0),(0,n.Lk)("input",{id:"file-upload",ref:"fileUploadInput",type:"file",accept:S.acceptedTypes,multiple:"true",tabindex:"-1","aria-hidden":"true",class:"hidden-visually",onChange:e[0]||(e[0]=t=>S.handleFileChange(t))},null,40,s),(0,n.Lk)("div",l,[(0,n.bF)(O,{user:L.currentUser.uid,displayName:L.currentUser.displayName,disableMenu:!0,disableTooltip:!0,size:32},null,8,["user","displayName"]),(0,n.Lk)("div",A,[(0,n.Lk)("span",c,(0,r.v_)(L.currentUser.displayName),1)])]),z.replyTo&&!S.anchoredReply?((0,n.uX)(),(0,n.CE)("div",d,[(0,n.Lk)("p",p,[(0,n.Lk)("span",null,(0,r.v_)(t.t("social","In reply to")),1),(0,n.bF)(N,{actor:z.replyTo.account,size:16,link:!1},null,8,["actor"]),(0,n.Lk)("strong",null,(0,r.v_)(z.replyTo.account.acct),1),(0,n.bF)(W,{variant:"tertiary",class:"close-button","aria-label":t.t("social","Close reply"),onClick:S.closeReply},{icon:(0,n.k6)(()=>[(0,n.bF)($,{size:20})]),_:1},8,["aria-label","onClick"])]),(0,n.bF)(j,{item:z.replyTo},null,8,["item"])])):(0,n.Q3)("",!0),z.quoteOf?((0,n.uX)(),(0,n.CE)("div",u,[(0,n.Lk)("p",C,[(0,n.Lk)("span",null,(0,r.v_)(t.t("social","Quoting")),1),(0,n.bF)(N,{actor:z.quoteOf.account,size:16,link:!1},null,8,["actor"]),(0,n.Lk)("strong",null,(0,r.v_)(z.quoteOf.account.acct),1),(0,n.bF)(W,{variant:"tertiary",class:"close-button","aria-label":t.t("social","Remove quote"),onClick:S.removeQuote},{icon:(0,n.k6)(()=>[(0,n.bF)($,{size:20})]),_:1},8,["aria-label","onClick"])]),(0,n.bF)(j,{item:z.quoteOf},null,8,["item"])])):(0,n.Q3)("",!0),(0,n.Lk)("form",{class:(0,r.C4)(["new-post-form",{"new-post-form--media-first":S.hasAttachments}]),onSubmit:e[10]||(e[10]=(0,o.D$)(()=>{},["prevent"]))},[z.showWarning?(0,n.bo)(((0,n.uX)(),(0,n.CE)("input",{key:0,"onUpdate:modelValue":e[1]||(e[1]=t=>z.spoilerText=t),type:"text",class:"content-warning",maxlength:"200","aria-label":t.t("social","Content warning"),placeholder:t.t("social","Content warning, e.g. what the post is about")},null,8,m)),[[o.Jo,z.spoilerText]]):(0,n.Q3)("",!0),(0,n.bF)(M,{uploading:z.uploading,uploadProgress:z.uploadProgress,progressLabel:z.progressLabel,miniatures:z.attachments,onDeleted:S.deletePreview,onDescribe:S.describeAttachment,onCommitDescription:S.commitDescription,onFilter:S.applyFilter},null,8,["uploading","uploadProgress","progressLabel","miniatures","onDeleted","onDescribe","onCommitDescription","onFilter"]),(0,n.Lk)("div",{ref:"composerInput",contenteditable:!z.loading,class:(0,r.C4)(["message",{"icon-loading":z.loading,"too-long":S.statusIsTooLong,"message--caption":S.hasAttachments}]),role:"textbox","aria-multiline":"true","aria-label":S.prompt,"aria-describedby":S.statusIsTooLong?"composer-length":void 0,placeholder:S.prompt,onKeyup:e[2]||(e[2]=(0,o.jR)((0,o.D$)((...t)=>S.keyup&&S.keyup(...t),["prevent"]),["enter"])),onInput:e[3]||(e[3]=(...t)=>S.updateStatusContent&&S.updateStatusContent(...t)),onPaste:e[4]||(e[4]=(...t)=>S.handlePaste&&S.handlePaste(...t)),onTributeReplaced:e[5]||(e[5]=(...t)=>S.updatePostFromTribute&&S.updatePostFromTribute(...t))},null,42,h),z.showPoll?((0,n.uX)(),(0,n.CE)("div",g,[((0,n.uX)(!0),(0,n.CE)(n.FK,null,(0,n.pI)(z.pollOptions,(e,i)=>((0,n.uX)(),(0,n.CE)("div",{key:i,class:"poll-editor__option"},[(0,n.bo)((0,n.Lk)("input",{"onUpdate:modelValue":t=>z.pollOptions[i]=t,type:"text",placeholder:t.t("social","Poll option {number}",{number:i+1}),maxlength:"100"},null,8,b),[[o.Jo,z.pollOptions[i]]]),z.pollOptions.length>2?((0,n.uX)(),(0,n.Wv)(W,{key:0,variant:"tertiary","aria-label":t.t("social","Remove option"),onClick:(0,o.D$)(t=>z.pollOptions.splice(i,1),["prevent"])},{icon:(0,n.k6)(()=>[(0,n.bF)($,{size:18})]),_:1},8,["aria-label","onClick"])):(0,n.Q3)("",!0)]))),128)),(0,n.Lk)("div",v,[z.pollOptions.length<4?((0,n.uX)(),(0,n.Wv)(W,{key:0,variant:"tertiary",onClick:e[6]||(e[6]=(0,o.D$)(t=>z.pollOptions.push(""),["prevent"]))},{default:(0,n.k6)(()=>[(0,n.eW)((0,r.v_)(t.t("social","Add option")),1)]),_:1})):(0,n.Q3)("",!0),(0,n.Lk)("label",null,[(0,n.bo)((0,n.Lk)("input",{"onUpdate:modelValue":e[7]||(e[7]=t=>z.pollMultiple=t),type:"checkbox"},null,512),[[o.lH,z.pollMultiple]]),(0,n.eW)(" "+(0,r.v_)(t.t("social","Multiple choice")),1)]),(0,n.bo)((0,n.Lk)("select",{"onUpdate:modelValue":e[8]||(e[8]=t=>z.pollExpiresIn=t),"aria-label":t.t("social","Poll duration")},[(0,n.Lk)("option",x,(0,r.v_)(t.t("social","30 minutes")),1),(0,n.Lk)("option",y,(0,r.v_)(t.t("social","1 hour")),1),(0,n.Lk)("option",w,(0,r.v_)(t.t("social","6 hours")),1),(0,n.Lk)("option",k,(0,r.v_)(t.t("social","1 day")),1),(0,n.Lk)("option",_,(0,r.v_)(t.t("social","3 days")),1),(0,n.Lk)("option",B,(0,r.v_)(t.t("social","7 days")),1)],8,f),[[o.u1,z.pollExpiresIn,void 0,{number:!0}]])])])):(0,n.Q3)("",!0),(0,n.Lk)("div",I,[(0,n.bF)(W,{title:t.t("social","Add attachment"),variant:"tertiary","aria-label":t.t("social","Add attachment"),disabled:S.attachmentsFull,onClick:(0,o.D$)(S.clickImportInput,["prevent"])},{icon:(0,n.k6)(()=>[(0,n.bF)(X,{size:22,decorative:"",title:""})]),_:1},8,["title","aria-label","disabled","onClick"]),(0,n.bF)(W,{title:t.t("social","Add from Files"),variant:"tertiary","aria-label":t.t("social","Add from Files"),disabled:S.attachmentsFull||z.picking,onClick:(0,o.D$)(S.pickFromFiles,["prevent"])},{icon:(0,n.k6)(()=>[(0,n.bF)(G,{size:22,decorative:"",title:""})]),_:1},8,["title","aria-label","disabled","onClick"]),(0,n.bF)(W,{title:z.showWarning?t.t("social","Remove content warning"):t.t("social","Add content warning"),variant:"tertiary","aria-label":z.showWarning?t.t("social","Remove content warning"):t.t("social","Add content warning"),"aria-pressed":z.showWarning,onClick:(0,o.D$)(S.toggleWarning,["prevent"])},{icon:(0,n.k6)(()=>[(0,n.bF)(U,{size:22,decorative:"",title:""})]),_:1},8,["title","aria-label","aria-pressed","onClick"]),(0,n.bF)(W,{title:z.showPoll?t.t("social","Remove poll"):t.t("social","Add poll"),variant:"tertiary","aria-label":z.showPoll?t.t("social","Remove poll"):t.t("social","Add poll"),onClick:(0,o.D$)(S.togglePoll,["prevent"])},{icon:(0,n.k6)(()=>[(0,n.bF)(q,{size:22,decorative:"",title:""})]),_:1},8,["title","aria-label","onClick"]),(0,n.Lk)("div",D,[z.emojiPickerLoaded?((0,n.uX)(),(0,n.Wv)(Y,{key:0,search:z.search,closeOnSelect:!1,container:"#content-vue",onSelect:S.insert},{default:(0,n.k6)(()=>[(0,n.bF)(W,{ref:"emojiButton",title:t.t("social","Add emoji"),variant:"tertiary","aria-haspopup":!0,"aria-label":t.t("social","Add emoji")},{icon:(0,n.k6)(()=>[(0,n.bF)(V,{size:22,decorative:"",title:""})]),_:1},8,["title","aria-label"])]),_:1},8,["search","onSelect"])):((0,n.uX)(),(0,n.Wv)(W,{key:1,title:t.t("social","Add emoji"),variant:"tertiary","aria-haspopup":!0,"aria-label":t.t("social","Add emoji"),onClick:S.loadEmojiPicker},{icon:(0,n.k6)(()=>[z.emojiPickerLoading?((0,n.uX)(),(0,n.Wv)(R,{key:0,size:22})):((0,n.uX)(),(0,n.Wv)(V,{key:1,size:22,decorative:"",title:""}))]),_:1},8,["title","aria-label","onClick"]))]),S.undescribed>0?((0,n.uX)(),(0,n.CE)("span",T,(0,r.v_)(S.undescribedWarning),1)):(0,n.Q3)("",!0),(0,n.bF)(Q,{visibility:z.visibility,"onUpdate:visibility":e[9]||(e[9]=t=>z.visibility=t)},null,8,["visibility"]),e[16]||(e[16]=(0,n.Lk)("div",{class:"emptySpace"},null,-1)),z.statusText.length>0?((0,n.uX)(),(0,n.CE)("span",{key:1,id:"composer-length",class:(0,r.C4)(["char-ring",{"char-ring--warning":S.charsLeft<=50,"char-ring--over":S.statusIsTooLong}]),style:(0,r.Tr)({"--char-progress":S.charProgress}),title:S.charactersLeftLabel},[S.charsLeft<=50?((0,n.uX)(),(0,n.CE)("span",P,(0,r.v_)(S.charsLeft),1)):(0,n.Q3)("",!0),(0,n.Lk)("span",E,(0,r.v_)(S.charsLeft<=50?S.charactersLeftLabel:""),1)],14,F)):(0,n.Q3)("",!0),(0,n.bF)(H,{visibility:z.visibility,disabled:!S.canPost||z.loading,onClick:S.createPost},null,8,["visibility","disabled","onClick"])])],34)],34)}},63883(t,e,i){i.d(e,{X:()=>c});var n=i(20641),o=i(90033);const r=["aria-label"],a=["aria-checked","title","onClick"],s={class:"filter-picker__swatch"},l=["src"],A={class:"filter-picker__name"};function c(t,e,i,c,d,p){return(0,n.uX)(),(0,n.CE)("div",{class:"filter-picker",role:"radiogroup","aria-label":p.t("social","Filter")},[((0,n.uX)(!0),(0,n.CE)(n.FK,null,(0,n.pI)(p.filters,e=>((0,n.uX)(),(0,n.CE)("button",{key:e.id,type:"button",role:"radio",class:(0,o.C4)(["filter-picker__option",{"filter-picker__option--active":e.id===i.modelValue}]),"aria-checked":String(e.id===i.modelValue),title:e.name,onClick:i=>t.$emit("update:modelValue",e.id)},[(0,n.Lk)("span",s,[i.preview?((0,n.uX)(),(0,n.CE)("img",{key:0,class:"filter-picker__image",src:i.preview,style:(0,o.Tr)({filter:e.css}),alt:"","aria-hidden":"true",decoding:"async"},null,12,l)):((0,n.uX)(),(0,n.CE)("span",{key:1,class:"filter-picker__placeholder",style:(0,o.Tr)({filter:e.css}),"aria-hidden":"true"},null,4))]),(0,n.Lk)("span",A,(0,o.v_)(e.name),1)],10,a))),128))],8,r)}},27289(t,e,i){i.d(e,{X:()=>c});var n=i(20641),o=i(90033);const r={class:"upload-form"},a={key:0,class:"upload-progress"},s={class:"upload-progress__icon"},l=["aria-label","aria-valuenow"],A={class:"upload-progress__backdrop"};function c(t,e,i,c,d,p){const u=(0,n.g2)("FileUpload"),C=(0,n.g2)("PreviewGridItem");return(0,n.uX)(),(0,n.CE)("div",r,[i.uploading?((0,n.uX)(),(0,n.CE)("div",a,[(0,n.Lk)("div",s,[(0,n.bF)(u,{size:32})]),(0,n.Lk)("div",{class:"upload-progress__message",role:"progressbar","aria-label":p.label,"aria-valuemin":"0","aria-valuemax":"100","aria-valuenow":Math.round(100*i.uploadProgress)},[(0,n.eW)((0,o.v_)(p.label)+" ",1),(0,n.Lk)("div",A,[(0,n.Lk)("div",{class:"upload-progress__tracker",style:(0,o.Tr)(`width: ${100*i.uploadProgress}%`)},null,4)])],8,l)])):(0,n.Q3)("",!0),(0,n.Lk)("div",{class:(0,o.C4)(["preview-grid",{"preview-grid--single":1===p.count}])},[((0,n.uX)(!0),(0,n.CE)(n.FK,null,(0,n.pI)(i.miniatures,(i,o)=>((0,n.uX)(),(0,n.Wv)(C,{key:o,preview:i,randomKey:o,onDelete:p.deletePreview,onDescribe:e[0]||(e[0]=e=>t.$emit("describe",e)),onCommitDescription:e[1]||(e[1]=e=>t.$emit("commitDescription",e)),onFilter:e[2]||(e[2]=e=>t.$emit("filter",e))},null,8,["preview","randomKey","onDelete"]))),128))],2)])}},53607(t,e,i){i.d(e,{X:()=>p});var n=i(20641),o=i(90033);const r={class:"preview-item-wrapper"},a={class:"preview-item"},s={class:"preview-item__actions"},l={key:0,class:"preview-item__missing","aria-hidden":"true"},A={key:1,class:"preview-item__failed",role:"status"},c=["for"],d=["id","value","placeholder"];function p(t,e,i,p,u,C){const m=(0,n.g2)("MediaAttachment"),h=(0,n.g2)("Close"),g=(0,n.g2)("NcButton"),b=(0,n.g2)("FilterPicker");return(0,n.uX)(),(0,n.CE)("div",r,[(0,n.Lk)("div",a,[(0,n.Lk)("div",{class:"preview-item__filtered",style:(0,o.Tr)({filter:C.filterCss(i.preview.filter)})},[i.preview.failed?(0,n.Q3)("",!0):((0,n.uX)(),(0,n.Wv)(m,{key:0,attachment:i.preview.data},null,8,["attachment"]))],4),(0,n.Lk)("div",s,[(0,n.bF)(g,{variant:"tertiary-no-background",onClick:e[0]||(e[0]=e=>t.$emit("delete",i.randomKey))},{icon:(0,n.k6)(()=>[(0,n.bF)(h,{size:16,fillColor:"white"})]),default:(0,n.k6)(()=>[(0,n.Lk)("span",null,(0,o.v_)(C.t("social","Delete")),1)]),_:1})]),C.described||i.preview.failed?(0,n.Q3)("",!0):((0,n.uX)(),(0,n.CE)("span",l,(0,o.v_)(C.t("social","No description")),1)),i.preview.failed?((0,n.uX)(),(0,n.CE)("span",A,(0,o.v_)(C.t("social","Could not be attached")),1)):(0,n.Q3)("",!0)]),!i.preview.failed&&C.isPicture&&i.preview.data?((0,n.uX)(),(0,n.Wv)(b,{key:0,class:"preview-item__filters",modelValue:i.preview.filter||"none",preview:C.previewUrl,"onUpdate:modelValue":e[1]||(e[1]=e=>t.$emit("filter",{key:i.randomKey,filter:e}))},null,8,["modelValue","preview"])):(0,n.Q3)("",!0),i.preview.failed?(0,n.Q3)("",!0):((0,n.uX)(),(0,n.CE)("label",{key:1,class:"preview-item__label",for:C.fieldId},(0,o.v_)(C.t("social","Describe this for people who cannot see it")),9,c)),i.preview.failed?(0,n.Q3)("",!0):((0,n.uX)(),(0,n.CE)("textarea",{key:2,id:C.fieldId,class:"preview-item__description",rows:"2",maxlength:"1500",value:i.preview.description||"",placeholder:C.t("social","A cat asleep on a keyboard"),onInput:e[2]||(e[2]=e=>t.$emit("describe",{key:i.randomKey,description:e.target.value})),onChange:e[3]||(e[3]=e=>t.$emit("commitDescription",{key:i.randomKey,description:e.target.value}))},null,40,d))])}},34173(t,e,i){i.d(e,{X:()=>a});var n=i(20641),o=i(53751),r=i(90033);function a(t,e,i,a,s,l){const A=(0,n.g2)("Send"),c=(0,n.g2)("NcButton");return(0,n.uX)(),(0,n.Wv)(c,{value:l.currentVisibilityPostLabel,disabled:i.disabled,variant:"primary",onClick:(0,o.D$)(l.handleClick,["prevent"])},{icon:(0,n.k6)(()=>[(0,n.bF)(A,{title:"",size:22,decorative:""})]),default:(0,n.k6)(()=>[(0,n.eW)(" "+(0,r.v_)(l.postTo),1)]),_:1},8,["value","disabled","onClick"])}},10083(t,e,i){i.d(e,{X:()=>r});var n=i(20641),o=i(90033);function r(t,e,i,r,a,s){const l=(0,n.g2)("VisibilityIcon"),A=(0,n.g2)("NcActionButton"),c=(0,n.g2)("NcActions");return(0,n.uX)(),(0,n.Wv)(c,{variant:"tertiary",menuName:s.selectedVisibilityDetails.text,"aria-label":s.t("social","Choose a visibility")},{icon:(0,n.k6)(()=>[(0,n.bF)(l,{visibility:s.selectedVisibilityDetails.id,size:20},null,8,["visibility"])]),default:(0,n.k6)(()=>[((0,n.uX)(!0),(0,n.CE)(n.FK,null,(0,n.pI)(a.visibilitiesInfo,t=>((0,n.uX)(),(0,n.Wv)(A,{key:t.id,class:(0,o.C4)({"selected-visibility":t.id===s.selectedVisibilityDetails.id}),closeAfterClick:!0,onClick:e=>s.switchType(t)},{icon:(0,n.k6)(()=>[(0,n.bF)(l,{visibility:t.id,size:20},null,8,["visibility"])]),default:(0,n.k6)(()=>[(0,n.eW)(" "+(0,o.v_)(t.description),1)]),_:2},1032,["class","onClick"]))),128))]),_:1},8,["menuName","aria-label"])}},16865(t,e,i){var n=i(20641);const o={mounted(t){(0,n.dY)(()=>{t.focus()})}};i.d(e,["A",0,o])},38161(t,e,i){i.d(e,{C_:()=>r,J1:()=>o,nr:()=>a});const n="social.composer.draft";function o({text:t,spoilerText:e="",visibility:i=""}){if(""===(t??"").trim()&&""===e.trim())return a();try{return window.localStorage.setItem(n,JSON.stringify({text:t,spoilerText:e,visibility:i,savedAt:Date.now()})),!0}catch{return!1}}function r(){let t,e;try{t=window.localStorage.getItem(n)}catch{return null}if(!t)return null;try{e=JSON.parse(t)}catch{return a(),null}return null===e||"object"!=typeof e||"string"!=typeof e.text||"number"==typeof e.savedAt&&Date.now()-e.savedAt>6048e5?(a(),null):{text:e.text,spoilerText:"string"==typeof e.spoilerText?e.spoilerText:"",visibility:"string"==typeof e.visibility?e.visibility:"",savedAt:"number"==typeof e.savedAt?e.savedAt:0}}function a(){try{return window.localStorage.removeItem(n),!0}catch{return!1}}},9699(t,e,i){i.d(e,{M8:()=>s,Tw:()=>a,Yo:()=>l});var n=i(71085);const o=[{id:"none",css:""},{id:"mono",css:"grayscale(1)"},{id:"noir",css:"grayscale(1) contrast(1.3) brightness(0.9)"},{id:"warm",css:"sepia(0.35) saturate(1.3) contrast(1.05)"},{id:"cool",css:"hue-rotate(-12deg) saturate(1.15) brightness(1.05)"},{id:"vivid",css:"saturate(1.6) contrast(1.1)"},{id:"faded",css:"saturate(0.75) contrast(0.9) brightness(1.1)"},{id:"sepia",css:"sepia(0.8)"}];function r(t){switch(t){case"none":default:return(0,n.t)("social","Original");case"mono":return(0,n.t)("social","Mono");case"noir":return(0,n.t)("social","Noir");case"warm":return(0,n.t)("social","Warm");case"cool":return(0,n.t)("social","Cool");case"vivid":return(0,n.t)("social","Vivid");case"faded":return(0,n.t)("social","Faded");case"sepia":return(0,n.t)("social","Sepia")}}function a(){return o.map(t=>({...t,name:r(t.id)}))}function s(t){return o.find(e=>e.id===t)?.css??""}async function l(t,e){const i=s(e);if(""===i||!t||!t.type?.startsWith("image/"))return t;if("image/gif"===t.type||"image/webp"===t.type)return t;try{const e=await createImageBitmap(t),n=document.createElement("canvas");n.width=e.width,n.height=e.height;const o=n.getContext("2d");if(null===o||!("filter"in o))return t;o.filter=i,o.drawImage(e,0,0),"function"==typeof e.close&&e.close();const r="image/png"===t.type?"image/png":"image/jpeg",a=await new Promise(t=>{n.toBlob(t,r,.92)});return null===a?t:new File([a],function(t,e){const i=(t||"image").replace(/\.[^.]+$/,""),n="image/png"===e?"png":"jpg";return`${i}.${n}`}(t.name,r),{type:r,lastModified:t.lastModified})}catch{return t}}},25113(t,e,i){var n=i(71354),o=i.n(n),r=i(76314),a=i.n(r),s=i(4417),l=i.n(s),A=new URL(i(92280),i.b),c=a()(o()),d=l()(A);c.push([t.id,`.new-post[data-v-0491766e]{background:var(--color-main-background);border:2px solid var(--color-border);border-radius:var(--border-radius-large, 12px);padding:18px;margin:calc(var(--default-grid-baseline)*3) auto;max-width:var(--social-column);position:sticky;top:0;z-index:100;box-shadow:var(--social-elevation-resting);transition:padding 220ms cubic-bezier(0.25, 0.8, 0.35, 1),border-color 220ms cubic-bezier(0.25, 0.8, 0.35, 1),background-color 220ms cubic-bezier(0.25, 0.8, 0.35, 1),box-shadow 220ms cubic-bezier(0.25, 0.8, 0.35, 1)}.new-post[data-v-0491766e]:focus-within{box-shadow:var(--social-elevation-raised)}.new-post-form[data-v-0491766e]{margin-top:12px;margin-inline-start:0;transition:margin-top 220ms cubic-bezier(0.25, 0.8, 0.35, 1)}.new-post-form__emoji-picker[data-v-0491766e]{z-index:1}.new-post--collapsed[data-v-0491766e]{display:flex;align-items:center;gap:12px;padding:10px 12px}.new-post--collapsed .new-post-author[data-v-0491766e]{padding-bottom:0;margin-bottom:0;border-bottom:none}.new-post--collapsed .new-post-form[data-v-0491766e]{flex:1 1 auto;min-width:0;margin-top:0}.new-post--collapsed .message[data-v-0491766e]{min-height:0;padding:9px 16px;border-radius:999px}.new-post--collapsed .options[data-v-0491766e]{max-height:0;margin-top:0;opacity:0;visibility:hidden;pointer-events:none;transform:translateY(-4px)}.new-post--drop-target[data-v-0491766e]{border-color:var(--color-primary-element);background:var(--color-primary-element-light, var(--color-background-hover))}.new-post--drop-target .message[data-v-0491766e]{border-color:var(--color-primary-element)}.new-post__drop-hint[data-v-0491766e]{position:absolute;inset:0;z-index:2;display:flex;align-items:center;justify-content:center;border-radius:inherit;pointer-events:none}.new-post__drop-hint span[data-v-0491766e]{padding:6px 14px;border-radius:var(--border-radius-pill, 999px);background:var(--color-primary-element);color:var(--color-primary-element-text, var(--color-primary-text));font-size:13px;font-weight:600;box-shadow:var(--social-elevation-raised);animation:composer-drop-hint-0491766e 220ms cubic-bezier(0.25, 0.8, 0.35, 1)}@keyframes composer-drop-hint-0491766e{0%{opacity:0;transform:scale(0.94)}100%{opacity:1;transform:scale(1)}}@keyframes composer-refused-0491766e{0%,100%{transform:translateX(0)}25%{transform:translateX(-4px)}75%{transform:translateX(4px)}}.new-post--refused[data-v-0491766e]{animation:composer-refused-0491766e 400ms cubic-bezier(0.25, 0.8, 0.35, 1)}@media(prefers-reduced-motion: reduce){.new-post[data-v-0491766e],.new-post .new-post-form[data-v-0491766e],.new-post .message[data-v-0491766e],.new-post .options[data-v-0491766e]{transition:none}.new-post--refused[data-v-0491766e],.new-post__drop-hint span[data-v-0491766e]{animation:none}}.new-post-author[data-v-0491766e]{display:flex;align-items:center;gap:10px;padding-bottom:10px;border-bottom:1px solid var(--color-border);margin-bottom:10px}.new-post-author .post-author[data-v-0491766e]{display:flex;align-items:center}.new-post-author .post-author .post-author-name[data-v-0491766e]{font-weight:700;font-size:14px;line-height:1.3}.reply-to[data-v-0491766e]{background:var(--color-background-hover);border-radius:8px;padding:12px 12px 12px 36px;margin-bottom:12px;position:relative}.reply-to[data-v-0491766e]::before{content:"";position:absolute;inset-inline-start:12px;top:12px;width:16px;height:16px;background-image:url(${d});background-size:contain;background-repeat:no-repeat}.reply-to .avatardiv[data-v-0491766e]{margin:0 4px;vertical-align:middle}.reply-to .reply-info[data-v-0491766e]{display:flex;align-items:center;gap:4px;font-size:13px;color:var(--color-text-lighter);margin-bottom:4px}.reply-to .close-button[data-v-0491766e]{margin-inline-start:auto;min-width:28px;min-height:28px;height:28px;width:28px !important}.quote-of[data-v-0491766e]{background:var(--color-background-hover);border-inline-start:3px solid var(--color-border-dark);border-radius:8px;padding:12px;margin-bottom:12px;font-size:14px}.quote-of .avatardiv[data-v-0491766e]{margin:0 4px;vertical-align:middle}.quote-of .quote-info[data-v-0491766e]{display:flex;align-items:center;gap:4px;font-size:13px;color:var(--color-text-lighter);margin-bottom:4px}.quote-of .close-button[data-v-0491766e]{margin-inline-start:auto;min-width:28px;min-height:28px;height:28px;width:28px !important}.message[data-v-0491766e]{width:100%;min-height:80px;padding:12px 14px;border:1px solid var(--color-border);border-radius:8px;transition:min-height 220ms cubic-bezier(0.25, 0.8, 0.35, 1),padding 220ms cubic-bezier(0.25, 0.8, 0.35, 1),border-radius 220ms cubic-bezier(0.25, 0.8, 0.35, 1),border-color 220ms cubic-bezier(0.25, 0.8, 0.35, 1);background:var(--color-main-background);font-size:14px;line-height:1.6;color:var(--color-main-text)}.message[data-v-0491766e]:focus-visible{border-color:var(--color-primary-element);outline:2px solid var(--color-primary-element);outline-offset:1px}.message.too-long[data-v-0491766e]{color:var(--color-error);border-color:var(--color-error)}.message[data-v-0491766e] .mention{color:var(--color-primary-element);background-color:var(--color-background-dark);border-radius:4px;padding:1px 6px 1px 2px;display:inline-flex;align-items:center}.message[data-v-0491766e] .mention img{width:16px;height:16px;border-radius:50%;margin-inline-end:3px}.new-post-form--media-first[data-v-0491766e] .preview-grid{margin-bottom:10px}.new-post-form--media-first .message--caption[data-v-0491766e]{min-height:44px}[contenteditable=true][data-v-0491766e]:empty:before{content:attr(placeholder);display:block;color:var(--color-text-lighter)}.options[data-v-0491766e]{display:flex;align-items:center;gap:8px;margin-top:10px;max-height:60px;opacity:1;overflow:hidden;transform:translateY(0);transition:max-height 220ms cubic-bezier(0.25, 0.8, 0.35, 1),margin-top 220ms cubic-bezier(0.25, 0.8, 0.35, 1),opacity 220ms cubic-bezier(0.25, 0.8, 0.35, 1),transform 220ms cubic-bezier(0.25, 0.8, 0.35, 1),visibility 220ms step-start}.emptySpace[data-v-0491766e]{flex-grow:1}@media(max-width: 600px){.options[data-v-0491766e]{flex-wrap:wrap;row-gap:4px;max-height:120px}.options .emptySpace[data-v-0491766e]{display:none}.options[data-v-0491766e] .action-item__menutoggle .button-vue__text{display:none}.options[data-v-0491766e]>:last-child{margin-inline-start:auto}}.hashtag[data-v-0491766e]{color:var(--color-primary-element);text-decoration:none}`,"",{version:3,sources:["webpack://./src/components/Composer/Composer.vue"],names:[],mappings:"AAMA,2BACC,uCAAA,CACA,oCAAA,CACA,8CAAA,CACA,YAAA,CACA,gDAAA,CAIA,8BAAA,CACA,eAAA,CACA,KAAA,CACA,WAAA,CACA,0CAAA,CACA,uNACC,CAOD,wCACC,yCAAA,CAGD,gCACC,eAAA,CACA,qBAAA,CACA,4DAAA,CAEA,8CACC,SAAA,CAQH,sCACC,YAAA,CACA,kBAAA,CACA,QAAA,CACA,iBAAA,CAEA,uDACC,gBAAA,CACA,eAAA,CACA,kBAAA,CAGD,qDACC,aAAA,CACA,WAAA,CACA,YAAA,CAGD,+CACC,YAAA,CACA,gBAAA,CACA,mBAAA,CAGD,+CACC,YAAA,CACA,YAAA,CACA,SAAA,CACA,iBAAA,CACA,mBAAA,CACA,0BAAA,CAQF,wCACC,yCAAA,CACA,4EAAA,CAEA,iDACC,yCAAA,CAIF,sCACC,iBAAA,CACA,OAAA,CACA,SAAA,CACA,YAAA,CACA,kBAAA,CACA,sBAAA,CACA,qBAAA,CAGA,mBAAA,CAEA,2CACC,gBAAA,CACA,8CAAA,CACA,uCAAA,CACA,kEAAA,CACA,cAAA,CACA,eAAA,CACA,yCAAA,CACA,4EAAA,CAIF,uCACC,GAAA,SAAA,CAAA,qBAAA,CACA,KAAA,SAAA,CAAA,kBAAA,CAAA,CAID,qCACC,QAAA,uBAAA,CACA,IAAA,0BAAA,CACA,IAAA,yBAAA,CAAA,CAGD,oCAEC,0EAAA,CAGD,uCACC,6IAIC,eAAA,CAGD,+EAEC,cAAA,CAAA,CAIF,kCACC,YAAA,CACA,kBAAA,CACA,QAAA,CACA,mBAAA,CACA,2CAAA,CACA,kBAAA,CAEA,+CACC,YAAA,CACA,kBAAA,CAEA,iEACC,eAAA,CACA,cAAA,CACA,eAAA,CAKH,2BACC,wCAAA,CACA,iBAAA,CACA,2BAAA,CACA,kBAAA,CACA,iBAAA,CAEA,mCACC,UAAA,CACA,iBAAA,CACA,uBAAA,CACA,QAAA,CACA,UAAA,CACA,WAAA,CACA,wDAAA,CACA,uBAAA,CACA,2BAAA,CAGD,sCACC,YAAA,CACA,qBAAA,CAGD,uCACC,YAAA,CACA,kBAAA,CACA,OAAA,CACA,cAAA,CACA,+BAAA,CACA,iBAAA,CAGD,yCACC,wBAAA,CACA,cAAA,CACA,eAAA,CACA,WAAA,CACA,qBAAA,CAKF,2BACC,wCAAA,CACA,sDAAA,CACA,iBAAA,CACA,YAAA,CACA,kBAAA,CACA,cAAA,CAEA,sCACC,YAAA,CACA,qBAAA,CAGD,uCACC,YAAA,CACA,kBAAA,CACA,OAAA,CACA,cAAA,CACA,+BAAA,CACA,iBAAA,CAGD,yCACC,wBAAA,CACA,cAAA,CACA,eAAA,CACA,WAAA,CACA,qBAAA,CAIF,0BACC,UAAA,CACA,eAAA,CACA,iBAAA,CACA,oCAAA,CACA,iBAAA,CACA,oNACC,CAID,uCAAA,CACA,cAAA,CACA,eAAA,CACA,4BAAA,CACA,wCACC,yCAAA,CACA,8CAAA,CACA,kBAAA,CAGD,mCACC,wBAAA,CACA,+BAAA,CAGD,mCACC,kCAAA,CACA,6CAAA,CACA,iBAAA,CACA,uBAAA,CACA,mBAAA,CACA,kBAAA,CAEA,uCACC,UAAA,CACA,WAAA,CACA,iBAAA,CACA,qBAAA,CAUF,2DACC,kBAAA,CAGD,+DACC,eAAA,CAIF,qDACC,yBAAA,CACA,aAAA,CACA,+BAAA,CAGD,0BACC,YAAA,CACA,kBAAA,CACA,OAAA,CACA,eAAA,CACA,eAAA,CACA,SAAA,CACA,eAAA,CACA,uBAAA,CACA,0OACC,CAOF,6BACC,WAAA,CASD,yBACC,0BACC,cAAA,CACA,WAAA,CACA,gBAAA,CAEA,sCACC,YAAA,CAGD,qEACC,YAAA,CAGD,sCACC,wBAAA,CAAA,CAKH,0BACC,kCAAA,CACA,oBAAA",sourcesContent:["\n// one duration and one curve for the whole opening, so the parts of it arrive\n// together rather than each on its own schedule\n$composer-ease: cubic-bezier(0.25, 0.8, 0.35, 1);\n$composer-duration: 220ms;\n\n.new-post {\n\tbackground: var(--color-main-background);\n\tborder: 2px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 12px);\n\tpadding: 18px;\n\tmargin: calc(var(--default-grid-baseline) * 3) auto;\n\t// the full column, where the list only fills it inside its own gutter: the\n\t// box you write in reaches a little past the posts it will join on both\n\t// sides, so it reads as the thing that makes them rather than one of them\n\tmax-width: var(--social-column);\n\tposition: sticky;\n\ttop: 0;\n\tz-index: 100;\n\tbox-shadow: var(--social-elevation-resting);\n\ttransition:\n\t\tpadding $composer-duration $composer-ease,\n\t\tborder-color $composer-duration $composer-ease,\n\t\tbackground-color $composer-duration $composer-ease,\n\t\tbox-shadow $composer-duration $composer-ease;\n\n\t// lifted, not outlined: the box the caret is in draws the ring, and two\n\t// nested rings around the same caret is one too many\n\t&:focus-within {\n\t\tbox-shadow: var(--social-elevation-raised);\n\t}\n\n\t&-form {\n\t\tmargin-top: 12px;\n\t\tmargin-inline-start: 0;\n\t\ttransition: margin-top $composer-duration $composer-ease;\n\n\t\t&__emoji-picker {\n\t\t\tz-index: 1;\n\t\t}\n\t}\n}\n\n// Closed: a portrait and a line to write on. Everything else is still in the\n// document — it is measured, not removed, so the opening can be animated — but\n// it is out of the tab order and out of the accessibility tree until it is.\n.new-post--collapsed {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\tpadding: 10px 12px;\n\n\t.new-post-author {\n\t\tpadding-bottom: 0;\n\t\tmargin-bottom: 0;\n\t\tborder-bottom: none;\n\t}\n\n\t.new-post-form {\n\t\tflex: 1 1 auto;\n\t\tmin-width: 0;\n\t\tmargin-top: 0;\n\t}\n\n\t.message {\n\t\tmin-height: 0;\n\t\tpadding: 9px 16px;\n\t\tborder-radius: 999px;\n\t}\n\n\t.options {\n\t\tmax-height: 0;\n\t\tmargin-top: 0;\n\t\topacity: 0;\n\t\tvisibility: hidden;\n\t\tpointer-events: none;\n\t\ttransform: translateY(-4px);\n\t}\n}\n\n// A file is being dragged over the card: the whole thing is the target, said\n// with a tint and the primary border it already uses for focus, plus one pill\n// naming what will happen. No dashes, no bounce — it is an invitation, not an\n// alarm, and it borrows the composer's own duration and curve.\n.new-post--drop-target {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element-light, var(--color-background-hover));\n\n\t.message {\n\t\tborder-color: var(--color-primary-element);\n\t}\n}\n\n.new-post__drop-hint {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 2;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tborder-radius: inherit;\n\t// it must never take the drop it is announcing, and it must not turn the\n\t// card into a storm of dragenter/dragleave by sitting under the pointer\n\tpointer-events: none;\n\n\tspan {\n\t\tpadding: 6px 14px;\n\t\tborder-radius: var(--border-radius-pill, 999px);\n\t\tbackground: var(--color-primary-element);\n\t\tcolor: var(--color-primary-element-text, var(--color-primary-text));\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tbox-shadow: var(--social-elevation-raised);\n\t\tanimation: composer-drop-hint $composer-duration $composer-ease;\n\t}\n}\n\n@keyframes composer-drop-hint {\n\t0% { opacity: 0; transform: scale(.94); }\n\t100% { opacity: 1; transform: scale(1); }\n}\n\n/* what was dropped is not something the composer can take */\n@keyframes composer-refused {\n\t0%, 100% { transform: translateX(0); }\n\t25% { transform: translateX(-4px); }\n\t75% { transform: translateX(4px); }\n}\n\n.new-post--refused {\n\t// 400ms, the same span REFUSAL_DURATION keeps the class on for\n\tanimation: composer-refused 400ms $composer-ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.new-post,\n\t.new-post .new-post-form,\n\t.new-post .message,\n\t.new-post .options {\n\t\ttransition: none;\n\t}\n\n\t.new-post--refused,\n\t.new-post__drop-hint span {\n\t\tanimation: none;\n\t}\n}\n\n.new-post-author {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tpadding-bottom: 10px;\n\tborder-bottom: 1px solid var(--color-border);\n\tmargin-bottom: 10px;\n\n\t.post-author {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\n\t\t.post-author-name {\n\t\t\tfont-weight: 700;\n\t\t\tfont-size: 14px;\n\t\t\tline-height: 1.3;\n\t\t}\n\t}\n}\n\n.reply-to {\n\tbackground: var(--color-background-hover);\n\tborder-radius: 8px;\n\tpadding: 12px 12px 12px 36px;\n\tmargin-bottom: 12px;\n\tposition: relative;\n\n\t&::before {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tinset-inline-start: 12px;\n\t\ttop: 12px;\n\t\twidth: 16px;\n\t\theight: 16px;\n\t\tbackground-image: url(../../../img/reply.svg);\n\t\tbackground-size: contain;\n\t\tbackground-repeat: no-repeat;\n\t}\n\n\t.avatardiv {\n\t\tmargin: 0 4px;\n\t\tvertical-align: middle;\n\t}\n\n\t.reply-info {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 4px;\n\t\tfont-size: 13px;\n\t\tcolor: var(--color-text-lighter);\n\t\tmargin-bottom: 4px;\n\t}\n\n\t.close-button {\n\t\tmargin-inline-start: auto;\n\t\tmin-width: 28px;\n\t\tmin-height: 28px;\n\t\theight: 28px;\n\t\twidth: 28px !important;\n\t}\n}\n\n/* set in and ruled off, the same way a quote reads in the timeline */\n.quote-of {\n\tbackground: var(--color-background-hover);\n\tborder-inline-start: 3px solid var(--color-border-dark);\n\tborder-radius: 8px;\n\tpadding: 12px;\n\tmargin-bottom: 12px;\n\tfont-size: 14px;\n\n\t.avatardiv {\n\t\tmargin: 0 4px;\n\t\tvertical-align: middle;\n\t}\n\n\t.quote-info {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 4px;\n\t\tfont-size: 13px;\n\t\tcolor: var(--color-text-lighter);\n\t\tmargin-bottom: 4px;\n\t}\n\n\t.close-button {\n\t\tmargin-inline-start: auto;\n\t\tmin-width: 28px;\n\t\tmin-height: 28px;\n\t\theight: 28px;\n\t\twidth: 28px !important;\n\t}\n}\n\n.message {\n\twidth: 100%;\n\tmin-height: 80px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 8px;\n\ttransition:\n\t\tmin-height $composer-duration $composer-ease,\n\t\tpadding $composer-duration $composer-ease,\n\t\tborder-radius $composer-duration $composer-ease,\n\t\tborder-color $composer-duration $composer-ease;\n\tbackground: var(--color-main-background);\n\tfont-size: 14px;\n\tline-height: 1.6;\n\tcolor: var(--color-main-text);\n\t&:focus-visible {\n\t\tborder-color: var(--color-primary-element);\n\t\toutline: 2px solid var(--color-primary-element);\n\t\toutline-offset: 1px;\n\t}\n\n\t&.too-long {\n\t\tcolor: var(--color-error);\n\t\tborder-color: var(--color-error);\n\t}\n\n\t:deep(.mention) {\n\t\tcolor: var(--color-primary-element);\n\t\tbackground-color: var(--color-background-dark);\n\t\tborder-radius: 4px;\n\t\tpadding: 1px 6px 1px 2px;\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\n\t\timg {\n\t\t\twidth: 16px;\n\t\t\theight: 16px;\n\t\t\tborder-radius: 50%;\n\t\t\tmargin-inline-end: 3px;\n\t\t}\n\t}\n}\n\n// Photo-first: the picture is the post and the box under it is its caption,\n// so the box gives up the height it was holding for a post that has no picture.\n// Nothing is moved or hidden — the same controls in the same order, weighted\n// the other way round.\n.new-post-form--media-first {\n\t:deep(.preview-grid) {\n\t\tmargin-bottom: 10px;\n\t}\n\n\t.message--caption {\n\t\tmin-height: 44px;\n\t}\n}\n\n[contenteditable=true]:empty:before {\n\tcontent: attr(placeholder);\n\tdisplay: block;\n\tcolor: var(--color-text-lighter);\n}\n\n.options {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin-top: 10px;\n\tmax-height: 60px;\n\topacity: 1;\n\toverflow: hidden;\n\ttransform: translateY(0);\n\ttransition:\n\t\tmax-height $composer-duration $composer-ease,\n\t\tmargin-top $composer-duration $composer-ease,\n\t\topacity $composer-duration $composer-ease,\n\t\ttransform $composer-duration $composer-ease,\n\t\tvisibility $composer-duration step-start;\n}\n\n.emptySpace {\n\tflex-grow: 1;\n}\n\n/*\n * A phone. Seven controls and a Post button do not fit one row of 358px, and\n * the row used to end with half the button off the screen. The row wraps: the\n * spacer goes, the visibility menu shows its icon only, and Post keeps the\n * end of whatever row it lands on.\n */\n@media (max-width: 600px) {\n\t.options {\n\t\tflex-wrap: wrap;\n\t\trow-gap: 4px;\n\t\tmax-height: 120px;\n\n\t\t.emptySpace {\n\t\t\tdisplay: none;\n\t\t}\n\n\t\t:deep(.action-item__menutoggle .button-vue__text) {\n\t\t\tdisplay: none;\n\t\t}\n\n\t\t> :last-child {\n\t\t\tmargin-inline-start: auto;\n\t\t}\n\t}\n}\n\n.hashtag {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n"],sourceRoot:""}]);const p=c;i.d(e,["A",0,p])},65943(t,e,i){var n=i(71354),o=i.n(n),r=i(76314),a=i.n(r)()(o());a.push([t.id,'.tribute-container{position:absolute;top:0;inset-inline-start:0;height:auto;max-height:300px;max-width:500px;min-width:200px;overflow:auto;display:block;z-index:999999;border-radius:8px;border:1px solid var(--color-border)}.tribute-container ul{margin:0;margin-top:2px;padding:4px;list-style:none;background:var(--color-main-background);border-radius:8px;background-clip:padding-box;overflow:hidden}.tribute-container ul li{color:var(--color-text);padding:6px 10px;cursor:pointer;font-size:14px;display:flex;border-radius:6px;margin:2px 0}.tribute-container ul li span{display:block;font-weight:bold}.tribute-container ul li.highlight,.tribute-container ul li:hover{background:var(--color-primary);color:var(--color-primary-text)}.tribute-container ul li img{width:32px;height:32px;border-radius:50%;overflow:hidden;margin-inline:-3px 10px;margin-top:3px}.tribute-container ul li.no-match{cursor:default}.tribute-container .menu-highlighted{font-weight:bold}.tribute-container .account,.tribute-container li.highlight .account,.tribute-container li:hover .account{font-weight:normal;color:var(--color-text-light)}.tribute-container li.highlight .account,.tribute-container li:hover .account{color:var(--color-primary-text) !important}.poll-editor{margin:8px 0;padding:8px;border:1px solid var(--color-border);border-radius:var(--border-radius)}.poll-editor__option{display:flex;gap:4px;margin-bottom:4px}.poll-editor__option input[type=text]{flex-grow:1}.poll-editor__settings{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.composer-alt-warning{align-self:center;padding:2px 8px;border-radius:var(--border-radius-pill);background:var(--color-warning);color:var(--color-warning-text, var(--color-main-text));font-size:12px;font-weight:600}.char-ring{position:relative;width:24px;height:24px;flex-shrink:0;align-self:center;border-radius:50%;background:conic-gradient(var(--color-primary-element) calc(var(--char-progress) * 360deg), var(--color-background-dark) 0);transition:background .2s ease}.char-ring::after{content:"";position:absolute;inset:3px;border-radius:50%;background:var(--color-main-background)}.char-ring--warning{background:conic-gradient(var(--color-warning) calc(var(--char-progress) * 360deg), var(--color-background-dark) 0)}.char-ring--over{background:var(--color-error)}.char-ring__count{position:absolute;inset:0;z-index:1;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:bold;color:var(--color-main-text)}@media(prefers-reduced-motion: reduce){.char-ring{transition:none}}.content-warning{width:100%;margin-bottom:6px;padding:8px 10px;border:1px solid var(--color-border);border-radius:var(--border-radius, 8px);background:var(--color-main-background);color:var(--color-main-text);font-size:14px}.content-warning:focus-visible{border-color:var(--color-primary-element);outline:2px solid var(--color-primary-element);outline-offset:1px}',"",{version:3,sources:["webpack://./src/components/Composer/Composer.vue"],names:[],mappings:"AACA,mBACC,iBAAA,CACA,KAAA,CACA,oBAAA,CACA,WAAA,CACA,gBAAA,CACA,eAAA,CACA,eAAA,CACA,aAAA,CACA,aAAA,CACA,cAAA,CACA,iBAAA,CACA,oCAAA,CAEA,sBACC,QAAA,CACA,cAAA,CACA,WAAA,CACA,eAAA,CACA,uCAAA,CACA,iBAAA,CACA,2BAAA,CACA,eAAA,CAEA,yBACC,uBAAA,CACA,gBAAA,CACA,cAAA,CACA,cAAA,CACA,YAAA,CACA,iBAAA,CACA,YAAA,CAEA,8BACC,aAAA,CACA,gBAAA,CAGD,kEAEC,+BAAA,CACA,+BAAA,CAGD,6BACC,UAAA,CACA,WAAA,CACA,iBAAA,CACA,eAAA,CACA,uBAAA,CACA,cAAA,CAGD,kCACC,cAAA,CAKH,qCACC,gBAAA,CAGD,0GAGC,kBAAA,CACA,6BAAA,CAGD,8EAEC,0CAAA,CAIF,aACC,YAAA,CACA,WAAA,CACA,oCAAA,CACA,kCAAA,CAEA,qBACC,YAAA,CACA,OAAA,CACA,iBAAA,CAEA,sCACC,WAAA,CAIF,uBACC,YAAA,CACA,kBAAA,CACA,QAAA,CACA,cAAA,CAIF,sBACC,iBAAA,CACA,eAAA,CACA,uCAAA,CACA,+BAAA,CACA,uDAAA,CACA,cAAA,CACA,eAAA,CAGD,WACC,iBAAA,CACA,UAAA,CACA,WAAA,CACA,aAAA,CACA,iBAAA,CACA,iBAAA,CACA,2HAAA,CAIA,8BAAA,CAEA,kBACC,UAAA,CACA,iBAAA,CACA,SAAA,CACA,iBAAA,CACA,uCAAA,CAGD,oBACC,mHAAA,CAMD,iBACC,6BAAA,CAGD,kBACC,iBAAA,CACA,OAAA,CACA,SAAA,CACA,YAAA,CACA,kBAAA,CACA,sBAAA,CACA,cAAA,CACA,gBAAA,CACA,4BAAA,CAIF,uCACC,WACC,eAAA,CAAA,CAIF,iBACC,UAAA,CACA,iBAAA,CACA,gBAAA,CACA,oCAAA,CACA,uCAAA,CACA,uCAAA,CACA,4BAAA,CACA,cAAA,CAEA,+BACC,yCAAA,CACA,8CAAA,CACA,kBAAA",sourcesContent:["\n.tribute-container {\n\tposition: absolute;\n\ttop: 0;\n\tinset-inline-start: 0;\n\theight: auto;\n\tmax-height: 300px;\n\tmax-width: 500px;\n\tmin-width: 200px;\n\toverflow: auto;\n\tdisplay: block;\n\tz-index: 999999;\n\tborder-radius: 8px;\n\tborder: 1px solid var(--color-border);\n\n\tul {\n\t\tmargin: 0;\n\t\tmargin-top: 2px;\n\t\tpadding: 4px;\n\t\tlist-style: none;\n\t\tbackground: var(--color-main-background);\n\t\tborder-radius: 8px;\n\t\tbackground-clip: padding-box;\n\t\toverflow: hidden;\n\n\t\tli {\n\t\t\tcolor: var(--color-text);\n\t\t\tpadding: 6px 10px;\n\t\t\tcursor: pointer;\n\t\t\tfont-size: 14px;\n\t\t\tdisplay: flex;\n\t\t\tborder-radius: 6px;\n\t\t\tmargin: 2px 0;\n\n\t\t\tspan {\n\t\t\t\tdisplay: block;\n\t\t\t\tfont-weight: bold;\n\t\t\t}\n\n\t\t\t&.highlight,\n\t\t\t&:hover {\n\t\t\t\tbackground: var(--color-primary);\n\t\t\t\tcolor: var(--color-primary-text);\n\t\t\t}\n\n\t\t\timg {\n\t\t\t\twidth: 32px;\n\t\t\t\theight: 32px;\n\t\t\t\tborder-radius: 50%;\n\t\t\t\toverflow: hidden;\n\t\t\t\tmargin-inline: -3px 10px;\n\t\t\t\tmargin-top: 3px;\n\t\t\t}\n\n\t\t\t&.no-match {\n\t\t\t\tcursor: default;\n\t\t\t}\n\t\t}\n\t}\n\n\t.menu-highlighted {\n\t\tfont-weight: bold;\n\t}\n\n\t.account,\n\tli.highlight .account,\n\tli:hover .account {\n\t\tfont-weight: normal;\n\t\tcolor: var(--color-text-light);\n\t}\n\n\tli.highlight .account,\n\tli:hover .account {\n\t\tcolor: var(--color-primary-text) !important;\n\t}\n}\n\n.poll-editor {\n\tmargin: 8px 0;\n\tpadding: 8px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\n\t&__option {\n\t\tdisplay: flex;\n\t\tgap: 4px;\n\t\tmargin-bottom: 4px;\n\n\t\tinput[type='text'] {\n\t\t\tflex-grow: 1;\n\t\t}\n\t}\n\n\t&__settings {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 12px;\n\t\tflex-wrap: wrap;\n\t}\n}\n/* the allowance as a ring that fills, rather than a limit you discover */\n.composer-alt-warning {\n\talign-self: center;\n\tpadding: 2px 8px;\n\tborder-radius: var(--border-radius-pill);\n\tbackground: var(--color-warning);\n\tcolor: var(--color-warning-text, var(--color-main-text));\n\tfont-size: 12px;\n\tfont-weight: 600;\n}\n\n.char-ring {\n\tposition: relative;\n\twidth: 24px;\n\theight: 24px;\n\tflex-shrink: 0;\n\talign-self: center;\n\tborder-radius: 50%;\n\tbackground: conic-gradient(\n\t\tvar(--color-primary-element) calc(var(--char-progress) * 360deg),\n\t\tvar(--color-background-dark) 0\n\t);\n\ttransition: background .2s ease;\n\n\t&::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tinset: 3px;\n\t\tborder-radius: 50%;\n\t\tbackground: var(--color-main-background);\n\t}\n\n\t&--warning {\n\t\tbackground: conic-gradient(\n\t\t\tvar(--color-warning) calc(var(--char-progress) * 360deg),\n\t\t\tvar(--color-background-dark) 0\n\t\t);\n\t}\n\n\t&--over {\n\t\tbackground: var(--color-error);\n\t}\n\n\t&__count {\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\tz-index: 1;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tfont-size: 10px;\n\t\tfont-weight: bold;\n\t\tcolor: var(--color-main-text);\n\t}\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.char-ring {\n\t\ttransition: none;\n\t}\n}\n\n.content-warning {\n\twidth: 100%;\n\tmargin-bottom: 6px;\n\tpadding: 8px 10px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius, 8px);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n\n\t&:focus-visible {\n\t\tborder-color: var(--color-primary-element);\n\t\toutline: 2px solid var(--color-primary-element);\n\t\toutline-offset: 1px;\n\t}\n}\n"],sourceRoot:""}]);const s=a;i.d(e,["A",0,s])},13360(t,e,i){var n=i(71354),o=i.n(n),r=i(76314),a=i.n(r)()(o());a.push([t.id,".filter-picker[data-v-64b107b5]{display:flex;gap:var(--default-grid-baseline);overflow-x:auto;padding-block:var(--default-grid-baseline);overscroll-behavior-x:contain}.filter-picker__option[data-v-64b107b5]{display:flex;flex-direction:column;align-items:center;gap:2px;padding:2px;border:none;background:none;cursor:pointer;flex:0 0 auto}.filter-picker__option[data-v-64b107b5]:focus-visible{outline:2px solid var(--color-primary-element);border-radius:var(--border-radius)}.filter-picker__swatch[data-v-64b107b5]{width:48px;height:48px;border-radius:var(--border-radius);overflow:hidden;border:2px solid rgba(0,0,0,0);background-color:var(--color-background-dark)}.filter-picker__option--active .filter-picker__swatch[data-v-64b107b5]{border-color:var(--color-primary-element)}.filter-picker__image[data-v-64b107b5],.filter-picker__placeholder[data-v-64b107b5]{width:100%;height:100%;object-fit:cover;display:block}.filter-picker__placeholder[data-v-64b107b5]{background:linear-gradient(135deg, var(--color-primary-element), var(--color-background-darker))}.filter-picker__name[data-v-64b107b5]{font-size:var(--font-size-small, 0.85em);color:var(--color-text-maxcontrast)}.filter-picker__option--active .filter-picker__name[data-v-64b107b5]{color:var(--color-main-text);font-weight:bold}","",{version:3,sources:["webpack://./src/components/Composer/FilterPicker.vue"],names:[],mappings:"AACA,gCACC,YAAA,CACA,gCAAA,CACA,eAAA,CACA,0CAAA,CAGA,6BAAA,CAEA,wCACC,YAAA,CACA,qBAAA,CACA,kBAAA,CACA,OAAA,CACA,WAAA,CACA,WAAA,CACA,eAAA,CACA,cAAA,CACA,aAAA,CAEA,sDACC,8CAAA,CACA,kCAAA,CAIF,wCACC,UAAA,CACA,WAAA,CACA,kCAAA,CACA,eAAA,CACA,8BAAA,CACA,6CAAA,CAEA,uEACC,yCAAA,CAIF,oFAEC,UAAA,CACA,WAAA,CACA,gBAAA,CACA,aAAA,CAGD,6CACC,gGAAA,CAGD,sCACC,wCAAA,CACA,mCAAA,CAEA,qEACC,4BAAA,CACA,gBAAA",sourcesContent:["\n.filter-picker {\n\tdisplay: flex;\n\tgap: var(--default-grid-baseline);\n\toverflow-x: auto;\n\tpadding-block: var(--default-grid-baseline);\n\t// a filter strip is a horizontal scroller by nature; keep it from stealing\n\t// the vertical scroll of the composer around it\n\toverscroll-behavior-x: contain;\n\n\t&__option {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\t\tgap: 2px;\n\t\tpadding: 2px;\n\t\tborder: none;\n\t\tbackground: none;\n\t\tcursor: pointer;\n\t\tflex: 0 0 auto;\n\n\t\t&:focus-visible {\n\t\t\toutline: 2px solid var(--color-primary-element);\n\t\t\tborder-radius: var(--border-radius);\n\t\t}\n\t}\n\n\t&__swatch {\n\t\twidth: 48px;\n\t\theight: 48px;\n\t\tborder-radius: var(--border-radius);\n\t\toverflow: hidden;\n\t\tborder: 2px solid transparent;\n\t\tbackground-color: var(--color-background-dark);\n\n\t\t.filter-picker__option--active & {\n\t\t\tborder-color: var(--color-primary-element);\n\t\t}\n\t}\n\n\t&__image,\n\t&__placeholder {\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tobject-fit: cover;\n\t\tdisplay: block;\n\t}\n\n\t&__placeholder {\n\t\tbackground: linear-gradient(135deg, var(--color-primary-element), var(--color-background-darker));\n\t}\n\n\t&__name {\n\t\tfont-size: var(--font-size-small, 0.85em);\n\t\tcolor: var(--color-text-maxcontrast);\n\n\t\t.filter-picker__option--active & {\n\t\t\tcolor: var(--color-main-text);\n\t\t\tfont-weight: bold;\n\t\t}\n\t}\n}\n"],sourceRoot:""}]);const s=a;i.d(e,["A",0,s])},61982(t,e,i){var n=i(71354),o=i.n(n),r=i(76314),a=i.n(r)()(o());a.push([t.id,".upload-progress[data-v-131432de]{display:flex;align-items:center;gap:10px;margin-bottom:8px}.upload-progress__message[data-v-131432de]{flex-grow:1;font-size:13px;color:var(--color-text-lighter)}.upload-progress__backdrop[data-v-131432de]{margin-top:4px;height:4px;border-radius:2px;background:var(--color-background-dark);overflow:hidden}.upload-progress__tracker[data-v-131432de]{height:100%;background:var(--color-primary-element);transition:width .2s ease}@media(prefers-reduced-motion: reduce){.upload-progress__tracker[data-v-131432de]{transition:none}}.preview-grid[data-v-131432de]{display:flex;flex-wrap:wrap;flex-direction:row;margin-inline:-5px}.preview-grid--single[data-v-131432de] .preview-item{height:260px}","",{version:3,sources:["webpack://./src/components/Composer/PreviewGrid.vue"],names:[],mappings:"AACA,kCACC,YAAA,CACA,kBAAA,CACA,QAAA,CACA,iBAAA,CAEA,2CACC,WAAA,CACA,cAAA,CACA,+BAAA,CAGD,4CACC,cAAA,CACA,UAAA,CACA,iBAAA,CACA,uCAAA,CACA,eAAA,CAGD,2CACC,WAAA,CACA,uCAAA,CACA,yBAAA,CAIF,uCACC,2CACC,eAAA,CAAA,CAIF,+BACC,YAAA,CACA,cAAA,CACA,kBAAA,CACA,kBAAA,CAID,qDACC,YAAA",sourcesContent:["\n.upload-progress {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tmargin-bottom: 8px;\n\n\t&__message {\n\t\tflex-grow: 1;\n\t\tfont-size: 13px;\n\t\tcolor: var(--color-text-lighter);\n\t}\n\n\t&__backdrop {\n\t\tmargin-top: 4px;\n\t\theight: 4px;\n\t\tborder-radius: 2px;\n\t\tbackground: var(--color-background-dark);\n\t\toverflow: hidden;\n\t}\n\n\t&__tracker {\n\t\theight: 100%;\n\t\tbackground: var(--color-primary-element);\n\t\ttransition: width .2s ease;\n\t}\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.upload-progress__tracker {\n\t\ttransition: none;\n\t}\n}\n\n.preview-grid {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tflex-direction: row;\n\tmargin-inline: -5px;\n}\n\n// one picture is the post, not a thumbnail of it\n.preview-grid--single :deep(.preview-item) {\n\theight: 260px;\n}\n"],sourceRoot:""}]);const s=a;i.d(e,["A",0,s])},35346(t,e,i){var n=i(71354),o=i.n(n),r=i(76314),a=i.n(r)()(o());a.push([t.id,".preview-item-wrapper[data-v-44df805c]{flex:1 1 0;min-width:40%;margin:5px}.preview-item__missing[data-v-44df805c]{position:absolute;inset-inline-start:8px;inset-block-end:8px;padding:2px 8px;border-radius:var(--border-radius-pill);background:var(--color-warning);color:var(--color-warning-text, var(--color-main-text));font-size:12px;font-weight:600}.preview-item__failed[data-v-44df805c]{position:absolute;inset-inline-start:8px;inset-block-end:8px;padding:2px 8px;border-radius:var(--border-radius-pill);background:var(--color-error);color:var(--color-primary-element-text, white);font-size:12px;font-weight:600}.preview-item__label[data-v-44df805c]{display:block;margin:6px 2px 2px;font-size:12px;color:var(--color-text-maxcontrast)}.preview-item__description[data-v-44df805c]{width:100%;min-height:44px;resize:vertical;box-sizing:border-box;border-radius:var(--border-radius);border:1px solid var(--color-border-dark);background:var(--color-main-background);color:var(--color-main-text);font-size:13px;padding:6px 8px}.preview-item__description[data-v-44df805c]:focus-visible{border-color:var(--color-primary-element);outline:2px solid var(--color-primary-element);outline-offset:1px}.preview-item[data-v-44df805c]{border-radius:var(--border-radius-large);background:var(--color-background-darker);background-position:50%;background-size:cover;background-repeat:no-repeat;height:140px;width:100%;overflow:hidden;position:relative}.preview-item .button-vue--tertiary-no-background[data-v-44df805c]{color:#fff !important}.preview-item__actions[data-v-44df805c]{position:absolute;top:0;width:100%;background:linear-gradient(180deg, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.35) 80%, transparent);display:flex;align-items:flex-start;justify-content:space-between}.preview-item__actions .button-vue__text[data-v-44df805c]{color:#fff !important}.preview-item .description-warning[data-v-44df805c]{position:absolute;z-index:2;bottom:0;inset-inline:0;box-sizing:border-box;background:linear-gradient(0deg, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.35) 80%, transparent);color:#fff;padding:10px}.modal__content[data-v-44df805c]{padding:20px}textarea[data-v-44df805c]{width:100%;height:100px;margin-bottom:20px}","",{version:3,sources:["webpack://./src/components/Composer/PreviewGridItem.vue"],names:[],mappings:"AACA,uCACC,UAAA,CACA,aAAA,CACA,UAAA,CAGD,wCACC,iBAAA,CACA,sBAAA,CACA,mBAAA,CACA,eAAA,CACA,uCAAA,CACA,+BAAA,CACA,uDAAA,CACA,cAAA,CACA,eAAA,CAGD,uCACC,iBAAA,CACA,sBAAA,CACA,mBAAA,CACA,eAAA,CACA,uCAAA,CACA,6BAAA,CACA,8CAAA,CACA,cAAA,CACA,eAAA,CAGD,sCACC,aAAA,CACA,kBAAA,CACA,cAAA,CACA,mCAAA,CAGD,4CACC,UAAA,CACA,eAAA,CACA,eAAA,CACA,qBAAA,CACA,kCAAA,CACA,yCAAA,CACA,uCAAA,CACA,4BAAA,CACA,cAAA,CACA,eAAA,CAEA,0DACC,yCAAA,CACA,8CAAA,CACA,kBAAA,CAIF,+BACC,wCAAA,CACA,yCAAA,CACA,uBAAA,CACA,qBAAA,CACA,2BAAA,CACA,YAAA,CACA,UAAA,CACA,eAAA,CACA,iBAAA,CAEA,mEACC,qBAAA,CAGD,wCACC,iBAAA,CACA,KAAA,CACA,UAAA,CACA,4FAAA,CACA,YAAA,CACA,sBAAA,CACA,6BAAA,CAEA,0DACC,qBAAA,CAIF,oDACC,iBAAA,CACA,SAAA,CACA,QAAA,CACA,cAAA,CACA,qBAAA,CACA,0FAAA,CACA,UAAA,CACA,YAAA,CAIF,iCACC,YAAA,CAGD,0BACC,UAAA,CACA,YAAA,CACA,kBAAA",sourcesContent:["\n.preview-item-wrapper {\n\tflex: 1 1 0;\n\tmin-width: 40%;\n\tmargin: 5px;\n}\n\n.preview-item__missing {\n\tposition: absolute;\n\tinset-inline-start: 8px;\n\tinset-block-end: 8px;\n\tpadding: 2px 8px;\n\tborder-radius: var(--border-radius-pill);\n\tbackground: var(--color-warning);\n\tcolor: var(--color-warning-text, var(--color-main-text));\n\tfont-size: 12px;\n\tfont-weight: 600;\n}\n\n.preview-item__failed {\n\tposition: absolute;\n\tinset-inline-start: 8px;\n\tinset-block-end: 8px;\n\tpadding: 2px 8px;\n\tborder-radius: var(--border-radius-pill);\n\tbackground: var(--color-error);\n\tcolor: var(--color-primary-element-text, white);\n\tfont-size: 12px;\n\tfont-weight: 600;\n}\n\n.preview-item__label {\n\tdisplay: block;\n\tmargin: 6px 2px 2px;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.preview-item__description {\n\twidth: 100%;\n\tmin-height: 44px;\n\tresize: vertical;\n\tbox-sizing: border-box;\n\tborder-radius: var(--border-radius);\n\tborder: 1px solid var(--color-border-dark);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tfont-size: 13px;\n\tpadding: 6px 8px;\n\n\t&:focus-visible {\n\t\tborder-color: var(--color-primary-element);\n\t\toutline: 2px solid var(--color-primary-element);\n\t\toutline-offset: 1px;\n\t}\n}\n\n.preview-item {\n\tborder-radius: var(--border-radius-large);\n\tbackground: var(--color-background-darker);\n\tbackground-position: 50%;\n\tbackground-size: cover;\n\tbackground-repeat: no-repeat;\n\theight: 140px;\n\twidth: 100%;\n\toverflow: hidden;\n\tposition: relative;\n\n\t.button-vue--tertiary-no-background {\n\t\tcolor: white !important;\n\t}\n\n\t&__actions {\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\twidth: 100%;\n\t\tbackground: linear-gradient(180deg,rgba(0,0,0,.8),rgba(0,0,0,.35) 80%,transparent);\n\t\tdisplay: flex;\n\t\talign-items: flex-start;\n\t\tjustify-content: space-between;\n\n\t\t.button-vue__text {\n\t\t\tcolor: white !important;\n\t\t}\n\t}\n\n\t.description-warning {\n\t\tposition: absolute;\n\t\tz-index: 2;\n\t\tbottom: 0;\n\t\tinset-inline: 0;\n\t\tbox-sizing: border-box;\n\t\tbackground: linear-gradient(0deg,rgba(0,0,0,.8),rgba(0,0,0,.35) 80%,transparent);\n\t\tcolor: white;\n\t\tpadding: 10px;\n\t}\n}\n\n.modal__content {\n\tpadding: 20px;\n}\n\ntextarea {\n\twidth: 100%;\n\theight: 100px;\n\tmargin-bottom: 20px;\n}\n"],sourceRoot:""}]);const s=a;i.d(e,["A",0,s])},47601(t,e,i){var n=i(71354),o=i.n(n),r=i(76314),a=i.n(r)()(o());a.push([t.id,"\n.selected-visibility[data-v-4cce609e] {\n\toutline: 1px solid var(--color-success);\n\tborder-radius: 6px;\n\tbackground: var(--color-background-hover);\n}\n","",{version:3,sources:["webpack://./src/components/Visibility/VisibilitySelect.vue"],names:[],mappings:";AAuFA;CACC,uCAAuC;CACvC,kBAAkB;CAClB,yCAAyC;AAC1C",sourcesContent:["\x3c!--\n - SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n--\x3e\n\n\n