Initial composer relayout

pull/1256/head
Lim Chee Aun 2025-09-02 20:32:44 +08:00
rodzic dbca3c5f39
commit fdc722f707
10 zmienionych plików z 856 dodań i 671 usunięć

Wyświetl plik

@ -16,7 +16,7 @@ export const ICONS = {
},
'arrow-up': () => import('@iconify-icons/mingcute/arrow-up-line'),
'arrow-down': () => import('@iconify-icons/mingcute/arrow-down-line'),
earth: () => import('@iconify-icons/mingcute/earth-line'),
earth: () => import('@iconify-icons/mingcute/world-2-line'),
lock: () => import('@iconify-icons/mingcute/lock-line'),
unlock: () => import('@iconify-icons/mingcute/unlock-line'),
'eye-close': () => import('@iconify-icons/mingcute/eye-close-line'),
@ -174,7 +174,7 @@ export const ICONS = {
settings: () => import('@iconify-icons/mingcute/settings-6-line'),
'heart-break': () => import('@iconify-icons/mingcute/heart-crack-line'),
'user-x': () => import('@iconify-icons/mingcute/user-x-line'),
minimize: () => import('@iconify-icons/mingcute/arrows-down-line'),
minimize: () => import('@iconify-icons/mingcute/down-line'),
celebrate: () => import('@iconify-icons/mingcute/celebrate-line'),
schedule: () => import('@iconify-icons/mingcute/calendar-time-add-line'),
day: () => import('@iconify-icons/mingcute/calendar-day-line'),

Wyświetl plik

@ -55,14 +55,15 @@ function ComposePoll({
</TextExpander>
<button
type="button"
class="plain2 poll-button"
class="plain4 poll-button"
disabled={disabled || options.length <= 1}
onClick={() => {
options.splice(i, 1);
onInput(poll);
}}
title={t`Remove`}
>
<Icon icon="x" size="s" alt={t`Remove`} />
</button>
</div>
))}
@ -76,56 +77,58 @@ function ComposePoll({
options.push('');
onInput(poll);
}}
title={t`Add`}
>
+
</button>{' '}
<label class="multiple-choices">
<input
type="checkbox"
checked={multiple}
<div class="poll-config">
<label class="multiple-choices">
<input
type="checkbox"
checked={multiple}
disabled={disabled}
onChange={(e) => {
const { checked } = e.target;
poll.multiple = checked;
onInput(poll);
}}
/>{' '}
<Trans>Multiple choice</Trans>
</label>
<label class="expires-in">
<Trans>Duration</Trans>{' '}
<select
value={expiresIn}
disabled={disabled}
onChange={(e) => {
const { value } = e.target;
poll.expiresIn = value;
onInput(poll);
}}
>
{Object.entries(expiryOptions)
.filter(([value]) => {
return value >= minExpiration && value <= maxExpiration;
})
.map(([value, label]) => (
<option value={value} key={value}>
{label()}
</option>
))}
</select>
</label>
<div class="spacer" />
<button
type="button"
class="light danger small"
disabled={disabled}
onChange={(e) => {
const { checked } = e.target;
poll.multiple = checked;
onInput(poll);
}}
/>{' '}
<Trans>Multiple choices</Trans>
</label>
<label class="expires-in">
<Trans>Duration</Trans>{' '}
<select
value={expiresIn}
disabled={disabled}
onChange={(e) => {
const { value } = e.target;
poll.expiresIn = value;
onInput(poll);
onClick={() => {
onInput(null);
}}
>
{Object.entries(expiryOptions)
.filter(([value]) => {
return value >= minExpiration && value <= maxExpiration;
})
.map(([value, label]) => (
<option value={value} key={value}>
{label()}
</option>
))}
</select>
</label>
</div>
<div class="poll-toolbar">
<button
type="button"
class="plain remove-poll-button"
disabled={disabled}
onClick={() => {
onInput(null);
}}
>
<Trans>Remove poll</Trans>
</button>
<Trans>Remove poll</Trans>
</button>
</div>
</div>
</div>
);

Wyświetl plik

@ -10,7 +10,7 @@
#compose-container {
margin: auto;
width: var(--main-width);
max-width: 100vw;
max-width: 100%;
align-self: stretch;
animation: fade-in 0.2s ease-out;
}
@ -27,7 +27,19 @@
white-space: nowrap;
@media (min-width: 480px) {
padding: 16px;
padding-block: 16px;
}
.compose-controls {
display: flex;
background-color: var(--bg-faded-blur-color);
backdrop-filter: blur(16px);
border-radius: 9999px;
border: 2px solid var(--bg-faded-color);
button:is(:hover, :focus) {
background-color: var(--bg-blur-color);
}
}
}
#compose-container .compose-top .account-block {
@ -41,6 +53,7 @@
backdrop-filter: blur(16px);
padding-inline-end: 1em;
border-radius: 9999px;
border: 2px solid var(--bg-faded-color);
}
@keyframes appear-up {
@ -57,12 +70,12 @@
border-radius: 16px 16px 0 0;
max-height: 160px;
background-color: var(--bg-color);
margin: 0 12px;
margin: 0 8px;
border: 1px solid var(--outline-color);
border-bottom: 0;
animation: appear-up 1s ease-in-out;
overflow: auto;
box-shadow: 0 -3px 12px -3px var(--drop-shadow-color);
box-shadow: 0 0px 12px -6px var(--drop-shadow-color);
}
#compose-container .status-preview:has(.status-badge:not(:empty)) {
border-start-end-radius: 8px;
@ -117,23 +130,23 @@
}
#compose-container form {
--form-spacing-inline: 4px;
--form-spacing-block: 0;
/* border-radius: 16px; */
--form-spacing-inline: 0;
--form-spacing-block: 8px;
padding: var(--form-spacing-block) var(--form-spacing-inline);
background-color: var(--bg-blur-color);
/* background-image: linear-gradient(var(--bg-color) 85%, transparent); */
position: relative;
z-index: 2;
--drop-shadow: 0 3px 6px -3px var(--drop-shadow-color);
--drop-shadow: 0 3px 6px -4px var(--drop-shadow-color);
box-shadow: var(--drop-shadow);
@media (min-width: 480px) {
--form-spacing-inline: 8px;
}
display: flex;
flex-direction: column;
gap: var(--form-spacing-block);
@media (min-width: 40em) {
--form-spacing-inline: 8px;
border-radius: 16px;
border-start-start-radius: 8px;
border-start-end-radius: 8px;
}
}
#compose-container .status-preview ~ form {
@ -145,29 +158,38 @@
#compose-container textarea {
width: 100%;
max-width: 100%;
height: 5em;
min-height: 5em;
max-height: 50vh;
resize: vertical;
line-height: 1.4;
border-color: transparent;
&.compose-field {
@media (min-width: 40em) {
max-height: 65vh;
}
&:hover {
border-color: var(--divider-color);
}
}
#compose-container textarea:hover {
border-color: var(--divider-color);
#compose-container .compose-field {
height: 5em;
height: var(--height);
min-height: 5em;
max-height: 50vh;
resize: vertical;
border-radius: 0;
@media (min-width: 40em) {
max-height: 65vh;
border-radius: 4px;
}
}
#compose-container .toolbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--form-spacing-inline) 0;
gap: var(--form-spacing-inline);
gap: 4px;
padding-inline: 8px;
@media (min-width: 40em) {
padding-inline: 0;
}
}
#compose-container .toolbar.wrap {
flex-wrap: wrap;
@ -175,31 +197,22 @@
#compose-container .toolbar.stretch {
justify-content: stretch;
}
#compose-container .toolbar {
.spoiler-text-field-container {
flex: 1;
min-width: 0;
.spoiler-text-field {
width: 100%;
}
}
}
#compose-container .toolbar-button {
display: inline-block;
color: var(--link-color);
color: var(--text-insignificant-color);
background-color: transparent;
padding: 0 8px;
border-radius: 8px;
min-height: 2.4em;
line-height: 2.4em;
min-width: 2.6em;
text-align: center;
overflow: hidden;
position: relative;
white-space: nowrap;
border: 2px solid transparent;
vertical-align: middle;
align-content: center;
word-wrap: normal;
overflow: hidden;
&.active {
filter: brightness(0.8);
@ -218,12 +231,11 @@
opacity: 0.5;
}
#compose-container
.toolbar-button:not(.show-field)
.toolbar-button
:is(input[type='checkbox'], select, input[type='file']) {
opacity: 0;
position: absolute;
left: 0;
height: 100%;
inset: 0;
margin: 0;
}
#compose-container .toolbar-button input[type='file'] {
@ -238,10 +250,6 @@
appearance: none;
line-height: 1em;
}
#compose-container .toolbar-button:not(.show-field) select {
inset-inline-end: 0;
inset-inline-start: auto !important;
}
#compose-container
.toolbar-button:not(:disabled):is(
:hover,
@ -254,10 +262,16 @@
background-color: var(--bg-color);
border-color: var(--link-faded-color);
outline: 0;
color: var(--link-color);
}
#compose-container .toolbar-button:not(:disabled).highlight {
border-color: var(--link-color);
box-shadow: inset 0 0 8px var(--link-faded-color);
color: var(--text-color);
border-color: var(--link-faded-color);
background-color: var(--bg-blur-color);
&:where(:focus-within, :hover) {
background-color: var(--link-bg-color);
}
}
#compose-container .toolbar-button:not(:disabled):active {
filter: brightness(0.8);
@ -266,17 +280,34 @@
#compose-container .toolbar-button .icon-text {
display: inline-block;
font-size: 14px;
font-weight: 500;
text-overflow: ellipsis;
overflow: hidden;
max-width: 100%;
min-width: 4ch;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
}
#compose-container .toolbar-divider {
width: 1px;
height: 100%;
margin: 0 var(--hairline-width);
background-image: linear-gradient(
to bottom,
transparent,
var(--divider-color),
transparent
);
flex-shrink: 0;
}
#compose-container .compose-footer {
.add-toolbar-button-group {
display: flex;
overflow: auto;
min-width: 42px;
}
.add-sub-toolbar-button-group {
flex-grow: 1;
@ -387,13 +418,16 @@
}
#compose-container .media-attachments {
background-color: var(--bg-faded-color);
background-color: var(--bg-faded-blur-color);
padding: 8px;
border-radius: 8px;
margin: 8px 0 0;
/* margin: 8px 0 0; */
display: flex;
flex-direction: column;
gap: 8px;
@media (min-width: 40em) {
border-radius: 8px;
}
}
#compose-container .media-attachment {
display: flex;
@ -473,19 +507,22 @@
justify-content: space-between;
align-items: center;
}
#compose-container .media-aside .close-button {
padding: 4px;
align-self: flex-start;
color: var(--text-insignificant-color);
}
#compose-container .media-aside .close-button:is(:hover, :focus) {
color: var(--text-color);
}
#compose-container .media-aside .uploaded {
color: var(--green-color);
margin-bottom: 4px;
}
#compose-container form .close-button {
padding: 4px;
align-self: center;
color: var(--text-insignificant-color);
&:is(:hover, :focus) {
color: var(--text-color);
background-color: var(--bg-color);
}
}
#compose-container .media-sensitive {
padding: 8px;
background-color: var(--bg-blur-color);
@ -497,10 +534,17 @@
}
#compose-container form .poll {
background-color: var(--bg-faded-color);
border-radius: 8px;
margin: 8px 0 0;
background-color: var(--bg-faded-blur-color);
background-image: none;
box-shadow: none;
border: 0;
margin: 0;
display: block;
border-radius: 0;
@media (min-width: 40em) {
border-radius: 8px;
}
}
#compose-container .poll-choices {
@ -533,7 +577,7 @@
flex-shrink: 0;
line-height: 0;
overflow: hidden;
transition: border-radius 1s ease-out;
transition: border-radius 0.5s ease-out;
font-size: 14px;
}
#compose-container .multiple .poll-button {
@ -546,45 +590,53 @@
align-items: stretch;
justify-content: space-between;
font-size: 90%;
border-top: 1px solid var(--outline-color);
border-top: 1px solid var(--bg-color);
padding: 8px;
}
#compose-container .poll-toolbar select {
padding: 4px;
}
#compose-container .multiple-choices {
#compose-container .poll-config {
flex-grow: 1;
display: flex;
flex-direction: row;
column-gap: 8px;
row-gap: 4px;
align-items: center;
flex-wrap: wrap;
}
#compose-container .multiple-choices {
display: flex;
gap: 4px;
align-items: center;
border-inline-start: 1px solid var(--outline-color);
padding-inline-start: 8px;
padding: 8px;
background-color: var(--bg-blur-color);
border-radius: 8px;
width: fit-content;
}
#compose-container .expires-in {
flex-grow: 1;
border-inline-start: 1px solid var(--outline-color);
padding-inline-start: 8px;
display: flex;
gap: 4px;
gap: 8px;
flex-wrap: wrap;
align-items: center;
justify-content: flex-end;
}
#compose-container .remove-poll-button {
width: 100%;
color: var(--red-color);
padding: 3px;
padding-inline-start: 8px;
background-color: var(--bg-blur-color);
border-radius: 8px;
width: fit-content;
}
#compose-container {
.scheduled-at {
background-color: var(--bg-faded-color);
border-radius: 8px;
margin: 8px 0 0;
justify-content: flex-end;
text-align: end;
background-color: var(--bg-faded-blur-color);
padding: 8px;
@media (min-width: 40em) {
border-radius: 8px;
}
input[type='datetime-local'] {
max-width: 80vw;
@ -636,12 +688,13 @@
}
#compose-container button[type='submit'] {
border-radius: 8px;
border-radius: 9px;
align-self: stretch;
@media (min-width: 480px) {
/* @media (min-width: 480px) {
padding-inline: 24px;
font-size: 125%;
}
} */
}
@keyframes breathe {
@ -932,16 +985,62 @@
}
}
.compose-cw-container {
display: flex;
&.collapsed {
display: none;
}
&:not(.collapsed) {
.spoiler-text-field-container {
flex: 1;
min-width: 0;
border-bottom: 2px dashed var(--divider-color);
.spoiler-text-field {
width: 100%;
padding-inline-end: calc(24px + 8px);
border-color: transparent;
border-radius: 0;
&:hover {
border-color: var(--divider-color);
border-style: solid;
}
@media (max-width: 40em) {
outline-offset: -2px;
}
@media (min-width: 40em) {
border-start-start-radius: 4px;
border-start-end-radius: 4px;
}
}
+ button {
position: absolute;
inset-inline-end: 8px;
@media (min-width: 40em) {
inset-inline-end: 16px;
}
}
}
+ .compose-field-container .compose-field {
border-start-start-radius: 0 !important;
border-start-end-radius: 0 !important;
}
}
}
.compose-field-container {
display: grid !important;
@media (width < 480px) {
margin-inline: calc(-1 * var(--form-spacing-inline));
width: 100vw !important;
max-width: 100vw;
@media (max-width: 40em) {
.compose-field {
border-radius: 0;
outline-offset: -2px;
}
}
@ -1067,7 +1166,7 @@
.add-button {
transform-origin: var(--forward) center;
background-color: var(--bg-blur-color) !important;
background-color: var(--bg-color) !important;
animation: jump-scare 0.2s ease-in-out both;
:dir(rtl) & {
animation-name: jump-scare-rtl;

Wyświetl plik

@ -2,7 +2,7 @@ import './compose.css';
import { msg, plural } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { MenuItem } from '@szhsin/react-menu';
import { MenuDivider, MenuItem } from '@szhsin/react-menu';
import { deepEqual } from 'fast-equals';
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { useHotkeys } from 'react-hotkeys-hook';
@ -31,6 +31,7 @@ import urlRegexObj from '../utils/url-regex';
import useCloseWatcher from '../utils/useCloseWatcher';
import useInterval from '../utils/useInterval';
import visibilityIconsMap from '../utils/visibility-icons-map';
import visibilityText from '../utils/visibility-text';
import AccountBlock from './account-block';
// import Avatar from './avatar';
@ -102,9 +103,12 @@ const ADD_LABELS = {
customEmoji: msg`Add custom emoji`,
gif: msg`Add GIF`,
poll: msg`Add poll`,
sensitive: msg`Add content warning`,
scheduledPost: msg`Schedule post`,
};
const DEFAULT_SCHEDULED_AT = Math.max(10 * 60 * 1000, MIN_SCHEDULED_AT); // 10 mins
function Compose({
onClose,
replyToStatus,
@ -160,6 +164,7 @@ function Compose({
const [visibility, setVisibility] = useState('public');
const [sensitive, setSensitive] = useState(false);
const [sensitiveMedia, setSensitiveMedia] = useState(false);
const [language, setLanguage] = useState(
store.session.get('currentLanguage') || DEFAULT_LANG,
);
@ -218,24 +223,45 @@ function Compose({
targetElement.dispatchEvent(new Event('input'));
};
const lastFocusedFieldRef = useRef(null);
const lastFocusedEmojiFieldRef = useRef(null);
const focusLastFocusedField = () => {
setTimeout(() => {
if (!lastFocusedFieldRef.current) return;
lastFocusedFieldRef.current.focus();
}, 0);
};
const composeContainerRef = useRef(null);
useEffect(() => {
const handleFocus = (e) => {
// Toggle focused if in or out if any fields are focused
composeContainerRef.current.classList.toggle(
'focused',
e.type === 'focusin',
);
const target = e.target;
if (target.hasAttribute('data-allow-custom-emoji')) {
lastFocusedEmojiFieldRef.current = target;
}
const isFormElement = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(
target.tagName,
);
if (isFormElement) {
lastFocusedFieldRef.current = target;
}
};
const composeContainer = composeContainerRef.current;
if (composeContainer) {
composeContainer.addEventListener('focusin', handleFocus);
composeContainer.addEventListener('focusout', handleFocus);
}
return () => {
if (composeContainer) {
composeContainer.removeEventListener('focusin', handleFocus);
composeContainer.removeEventListener('focusout', handleFocus);
}
};
}, []);
@ -328,6 +354,7 @@ function Compose({
visibility,
language,
sensitive,
sensitiveMedia,
poll,
mediaAttachments,
scheduledAt,
@ -347,6 +374,7 @@ function Compose({
prefs['posting:default:language']?.toLowerCase() ||
DEFAULT_LANG,
);
if (sensitiveMedia !== null) setSensitiveMedia(sensitiveMedia);
if (sensitive !== null) setSensitive(sensitive);
if (composablePoll) setPoll(composablePoll);
if (mediaAttachments) setMediaAttachments(mediaAttachments);
@ -538,6 +566,7 @@ function Compose({
visibility,
language,
sensitive,
sensitiveMedia,
poll,
mediaAttachments,
scheduledAt,
@ -707,12 +736,20 @@ function Compose({
states.composerState.minimized = true;
};
const gifPickerDisabled =
const mediaButtonDisabled =
uiState === 'loading' ||
(maxMediaAttachments !== undefined &&
mediaAttachments.length >= maxMediaAttachments) ||
!!poll;
const cwButtonDisabled = uiState === 'loading' || !!sensitive;
const onCWButtonClick = () => {
setSensitive(true);
setTimeout(() => {
spoilerTextRef.current?.focus();
}, 0);
};
// If maxOptions is not defined or defined and is greater than 1, show poll button
const showPollButton = maxOptions == null || maxOptions > 1;
const pollButtonDisabled =
@ -723,10 +760,23 @@ function Compose({
expiresIn: 24 * 60 * 60, // 1 day
multiple: false,
});
// Focus first choice field
setTimeout(() => {
composeContainerRef.current
?.querySelector('.poll-choice input[type="text"]')
?.focus();
}, 0);
};
const highlightLanguageField =
language !== prevLanguage.current ||
(autoDetectedLanguages?.length &&
!autoDetectedLanguages.includes(language));
const highlightVisibilityField = visibility !== 'public';
const addSubToolbarRef = useRef();
const [showAddButton, setShowAddButton] = useState(false);
const BUTTON_WIDTH = 42; // roughly one button width
useResizeObserver({
ref: addSubToolbarRef,
box: 'border-box',
@ -734,7 +784,7 @@ function Compose({
// If scrollable, it's truncated
const { scrollWidth } = addSubToolbarRef.current;
const truncated = scrollWidth > width;
const overTruncated = width < 84; // roughly two buttons width
const overTruncated = width < BUTTON_WIDTH * 4;
setShowAddButton(overTruncated || truncated);
addSubToolbarRef.current.hidden = overTruncated;
},
@ -743,13 +793,17 @@ function Compose({
const showScheduledAt = !editStatus;
const scheduledAtButtonDisabled = uiState === 'loading' || !!scheduledAt;
const onScheduledAtClick = () => {
const date = new Date(Date.now() + MIN_SCHEDULED_AT);
const date = new Date(Date.now() + DEFAULT_SCHEDULED_AT);
setScheduledAt(date);
};
return (
<div id="compose-container-outer" ref={composeContainerRef}>
<div id="compose-container" class={standalone ? 'standalone' : ''}>
<div
id="compose-container"
tabIndex={-1}
class={standalone ? 'standalone' : ''}
>
<div class="compose-top">
{currentAccountInfo?.avatarStatic && (
// <Avatar
@ -823,7 +877,7 @@ function Compose({
</button>{' '}
<button
type="button"
class="light close-button"
class="plain4 close-button"
disabled={uiState === 'loading'}
onClick={() => {
if (confirmClose()) {
@ -888,6 +942,7 @@ function Compose({
visibility,
language,
sensitive,
sensitiveMedia,
poll,
mediaAttachments,
scheduledAt,
@ -954,6 +1009,9 @@ function Compose({
pointerEvents: uiState === 'loading' ? 'none' : 'auto',
opacity: uiState === 'loading' ? 0.5 : 1,
}}
onClick={() => {
lastFocusedFieldRef.current?.focus?.();
}}
onKeyDown={(e) => {
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
formRef.current.dispatchEvent(
@ -967,11 +1025,19 @@ function Compose({
const formData = new FormData(e.target);
const entries = Object.fromEntries(formData.entries());
console.log('ENTRIES', entries);
let { status, visibility, sensitive, spoilerText, scheduledAt } =
entries;
let {
status,
visibility,
sensitive,
sensitiveMedia,
spoilerText,
scheduledAt,
} = entries;
// Pre-cleanup
sensitive = sensitive === 'on'; // checkboxes return "on" if checked
// checkboxes return "on" if checked
sensitive = sensitive === 'on';
sensitiveMedia = sensitiveMedia === 'on';
// Convert datetime-local input value to RFC3339 Date string value
scheduledAt = scheduledAt
@ -1085,7 +1151,7 @@ function Compose({
// spoilerText,
spoiler_text: spoilerText,
language,
sensitive,
sensitive: sensitive || sensitiveMedia,
poll,
// mediaIds: mediaAttachments.map((attachment) => attachment.id),
media_ids: mediaAttachments.map(
@ -1156,133 +1222,86 @@ function Compose({
})();
}}
>
<div class="toolbar stretch">
<TextExpander
keys=":"
class="spoiler-text-field-container"
<div>
<div class={`compose-cw-container ${sensitive ? '' : 'collapsed'}`}>
<TextExpander
keys=":"
class="spoiler-text-field-container"
onTrigger={(action) => {
if (action?.name === 'custom-emojis') {
setShowEmoji2Picker({
targetElement: spoilerTextRef,
defaultSearchTerm: action?.defaultSearchTerm || null,
});
}
}}
>
<input
ref={spoilerTextRef}
type="text"
name="spoilerText"
placeholder={t`Content warning`}
data-allow-custom-emoji="true"
disabled={uiState === 'loading'}
class="spoiler-text-field"
lang={language}
spellCheck="true"
autocomplete="off"
dir="auto"
onInput={() => {
updateCharCount();
}}
/>
</TextExpander>
<button
type="button"
class="close-button plain4 small"
onClick={() => {
setSensitive(false);
textareaRef.current.focus();
}}
>
<Icon icon="x" alt={t`Cancel`} />
</button>
</div>
<Textarea
ref={textareaRef}
data-allow-custom-emoji="true"
placeholder={
replyToStatus
? t`Post your reply`
: editStatus
? t`Edit your post`
: !!poll
? t`Ask a question`
: t`What are you doing?`
}
required={mediaAttachments?.length === 0}
disabled={uiState === 'loading'}
lang={language}
onInput={() => {
updateCharCount();
}}
maxCharacters={maxCharacters}
onTrigger={(action) => {
if (action?.name === 'custom-emojis') {
setShowEmoji2Picker({
targetElement: spoilerTextRef,
targetElement: lastFocusedEmojiFieldRef,
defaultSearchTerm: action?.defaultSearchTerm || null,
});
} else if (action?.name === 'mention') {
setShowMentionPicker({
defaultSearchTerm: action?.defaultSearchTerm || null,
});
} else if (
action?.name === 'auto-detect-language' &&
action?.languages
) {
setAutoDetectedLanguages(action.languages);
}
}}
>
<input
ref={spoilerTextRef}
type="text"
name="spoilerText"
placeholder={t`Content warning`}
data-allow-custom-emoji="true"
disabled={uiState === 'loading'}
class="spoiler-text-field"
lang={language}
spellCheck="true"
autocomplete="off"
dir="auto"
style={{
opacity: sensitive ? 1 : 0,
pointerEvents: sensitive ? 'auto' : 'none',
}}
onInput={() => {
updateCharCount();
}}
/>
</TextExpander>
<label
class={`toolbar-button ${sensitive ? 'highlight' : ''}`}
title={t`Content warning or sensitive media`}
>
<input
name="sensitive"
type="checkbox"
checked={sensitive}
disabled={uiState === 'loading'}
onChange={(e) => {
const sensitive = e.target.checked;
setSensitive(sensitive);
if (sensitive) {
spoilerTextRef.current?.focus();
} else {
textareaRef.current?.focus();
}
}}
/>
<Icon icon={`eye-${sensitive ? 'close' : 'open'}`} />
</label>{' '}
<label
class={`toolbar-button ${
visibility !== 'public' && !sensitive ? 'show-field' : ''
} ${visibility !== 'public' ? 'highlight' : ''}`}
title={visibility}
>
<Icon icon={visibilityIconsMap[visibility]} alt={visibility} />
<select
name="visibility"
value={visibility}
onChange={(e) => {
setVisibility(e.target.value);
}}
disabled={uiState === 'loading' || !!editStatus}
dir="auto"
>
<option value="public">
<Trans>Public</Trans>
</option>
{(supports('@pleroma/local-visibility-post') ||
supports('@akkoma/local-visibility-post')) && (
<option value="local">
<Trans>Local</Trans>
</option>
)}
<option value="unlisted">
<Trans>Unlisted</Trans>
</option>
<option value="private">
<Trans>Followers only</Trans>
</option>
<option value="direct">
<Trans>Private mention</Trans>
</option>
</select>
</label>{' '}
/>
</div>
<Textarea
ref={textareaRef}
data-allow-custom-emoji="true"
placeholder={
replyToStatus
? t`Post your reply`
: editStatus
? t`Edit your post`
: t`What are you doing?`
}
required={mediaAttachments?.length === 0}
disabled={uiState === 'loading'}
lang={language}
onInput={() => {
updateCharCount();
}}
maxCharacters={maxCharacters}
onTrigger={(action) => {
if (action?.name === 'custom-emojis') {
setShowEmoji2Picker({
targetElement: lastFocusedEmojiFieldRef,
defaultSearchTerm: action?.defaultSearchTerm || null,
});
} else if (action?.name === 'mention') {
setShowMentionPicker({
defaultSearchTerm: action?.defaultSearchTerm || null,
});
} else if (
action?.name === 'auto-detect-language' &&
action?.languages
) {
setAutoDetectedLanguages(action.languages);
}
}}
/>
{mediaAttachments?.length > 0 && (
<div class="media-attachments">
{mediaAttachments.map((attachment, i) => {
@ -1315,19 +1334,19 @@ function Compose({
})}
<label class="media-sensitive">
<input
name="sensitive"
name="sensitiveMedia"
type="checkbox"
checked={sensitive}
checked={sensitiveMedia}
disabled={uiState === 'loading'}
onChange={(e) => {
const sensitive = e.target.checked;
setSensitive(sensitive);
const sensitiveMedia = e.target.checked;
setSensitiveMedia(sensitiveMedia);
}}
/>{' '}
<span>
<Trans>Mark media as sensitive</Trans>
</span>{' '}
<Icon icon={`eye-${sensitive ? 'close' : 'open'}`} />
<Icon icon={`eye-${sensitiveMedia ? 'close' : 'open'}`} />
</label>
</div>
)}
@ -1346,32 +1365,37 @@ function Compose({
setPoll(newPoll);
} else {
setPoll(null);
focusLastFocusedField();
}
}}
/>
)}
{scheduledAt && (
<div class="toolbar scheduled-at">
<span>
<label>
<Trans>
Posting on{' '}
<ScheduledAtField
scheduledAt={scheduledAt}
setScheduledAt={setScheduledAt}
/>
</Trans>
</label>{' '}
<small class="tag insignificant">
{getLocalTimezoneName()}
</small>
</span>
<button
type="button"
class="plain4 small"
class="plain4 close-button small"
onClick={() => {
setScheduledAt(null);
focusLastFocusedField();
}}
>
<Icon icon="x" />
<Icon icon="x" alt={t`Cancel`} />
</button>
<label>
<Trans>
Posting on{' '}
<ScheduledAtField
scheduledAt={scheduledAt}
setScheduledAt={setScheduledAt}
/>
</Trans>
<br />
<small>{getLocalTimezoneName()}</small>
</label>
</div>
)}
<div class="toolbar compose-footer">
@ -1398,39 +1422,53 @@ function Compose({
)}
>
{supportsCameraCapture && (
<MenuItem className="compose-menu-add-media">
<MenuItem
disabled={mediaButtonDisabled}
className="compose-menu-add-media"
>
<label class="compose-menu-add-media-field">
<CameraCaptureInput
hidden
supportedMimeTypes={supportedImagesVideosTypes}
disabled={
uiState === 'loading' ||
mediaAttachments.length >= maxMediaAttachments ||
!!poll
}
disabled={mediaButtonDisabled}
setMediaAttachments={setMediaAttachments}
/>
</label>
<Icon icon="camera" /> <span>{_(ADD_LABELS.camera)}</span>
</MenuItem>
)}
<MenuItem className="compose-menu-add-media">
<MenuItem
disabled={mediaButtonDisabled}
className="compose-menu-add-media"
>
<label class="compose-menu-add-media-field">
<FilePickerInput
hidden
supportedMimeTypes={supportedMimeTypes}
maxMediaAttachments={maxMediaAttachments}
mediaAttachments={mediaAttachments}
disabled={
uiState === 'loading' ||
mediaAttachments.length >= maxMediaAttachments ||
!!poll
}
disabled={mediaButtonDisabled}
setMediaAttachments={setMediaAttachments}
/>
</label>
<Icon icon="media" /> <span>{_(ADD_LABELS.media)}</span>
</MenuItem>
<MenuItem
disabled={cwButtonDisabled}
onClick={onCWButtonClick}
>
<Icon icon={`eye-${sensitive ? 'close' : 'open'}`} />{' '}
<span>{_(ADD_LABELS.sensitive)}</span>
</MenuItem>
{showPollButton && (
<MenuItem
disabled={pollButtonDisabled}
onClick={onPollButtonClick}
>
<Icon icon="poll" /> <span>{_(ADD_LABELS.poll)}</span>
</MenuItem>
)}
<MenuDivider />
<MenuItem
onClick={() => {
setShowEmoji2Picker({
@ -1443,7 +1481,7 @@ function Compose({
</MenuItem>
{!!states.settings.composerGIFPicker && (
<MenuItem
disabled={gifPickerDisabled}
disabled={mediaButtonDisabled}
onClick={() => {
setShowGIFPicker(true);
}}
@ -1452,22 +1490,17 @@ function Compose({
<span>{_(ADD_LABELS.gif)}</span>
</MenuItem>
)}
{showPollButton && (
<MenuItem
disabled={pollButtonDisabled}
onClick={onPollButtonClick}
>
<Icon icon="poll" /> <span>{_(ADD_LABELS.poll)}</span>
</MenuItem>
)}
{showScheduledAt && (
<MenuItem
disabled={scheduledAtButtonDisabled}
onClick={onScheduledAtClick}
>
<Icon icon="schedule" />{' '}
<span>{_(ADD_LABELS.scheduledPost)}</span>
</MenuItem>
<>
<MenuDivider />
<MenuItem
disabled={scheduledAtButtonDisabled}
onClick={onScheduledAtClick}
>
<Icon icon="schedule" />{' '}
<span>{_(ADD_LABELS.scheduledPost)}</span>
</MenuItem>
</>
)}
</Menu2>
)}
@ -1477,11 +1510,7 @@ function Compose({
<CameraCaptureInput
supportedMimeTypes={supportedImagesVideosTypes}
mediaAttachments={mediaAttachments}
disabled={
uiState === 'loading' ||
mediaAttachments.length >= maxMediaAttachments ||
!!poll
}
disabled={mediaButtonDisabled}
setMediaAttachments={setMediaAttachments}
/>
<Icon icon="camera" alt={_(ADD_LABELS.camera)} />
@ -1492,25 +1521,43 @@ function Compose({
supportedMimeTypes={supportedMimeTypes}
maxMediaAttachments={maxMediaAttachments}
mediaAttachments={mediaAttachments}
disabled={
uiState === 'loading' ||
mediaAttachments.length >= maxMediaAttachments ||
!!poll
}
disabled={mediaButtonDisabled}
setMediaAttachments={setMediaAttachments}
/>
<Icon icon="media" alt={_(ADD_LABELS.media)} />
</label>
<button
type="button"
class="toolbar-button"
disabled={cwButtonDisabled}
onClick={onCWButtonClick}
>
<Icon
icon={`eye-${sensitive ? 'close' : 'open'}`}
alt={_(ADD_LABELS.sensitive)}
/>
</button>
{showPollButton && (
<button
type="button"
class="toolbar-button"
disabled={pollButtonDisabled}
onClick={onPollButtonClick}
>
<Icon icon="poll" alt={_(ADD_LABELS.poll)} />
</button>
)}
<div class="toolbar-divider" />
{/* <button
type="button"
class="toolbar-button"
disabled={uiState === 'loading'}
onClick={() => {
setShowMentionPicker(true);
}}
>
<Icon icon="at" />
</button> */}
type="button"
class="toolbar-button"
disabled={uiState === 'loading'}
onClick={() => {
setShowMentionPicker(true);
}}
>
<Icon icon="at" />
</button> */}
<button
type="button"
class="toolbar-button"
@ -1527,7 +1574,7 @@ function Compose({
<button
type="button"
class="toolbar-button gif-picker-button"
disabled={gifPickerDisabled}
disabled={mediaButtonDisabled}
onClick={() => {
setShowGIFPicker(true);
}}
@ -1538,31 +1585,21 @@ function Compose({
/>
</button>
)}
{showPollButton && (
{showScheduledAt && (
<>
<div class="toolbar-divider" />
<button
type="button"
class="toolbar-button"
disabled={pollButtonDisabled}
onClick={onPollButtonClick}
class={`toolbar-button ${scheduledAt ? 'highlight' : ''}`}
disabled={scheduledAtButtonDisabled}
onClick={onScheduledAtClick}
>
<Icon icon="poll" alt={_(ADD_LABELS.poll)} />
<Icon icon="schedule" alt={_(ADD_LABELS.scheduledPost)} />
</button>
</>
)}
{showScheduledAt && (
<button
type="button"
class={`toolbar-button ${scheduledAt ? 'highlight' : ''}`}
disabled={scheduledAtButtonDisabled}
onClick={onScheduledAtClick}
>
<Icon icon="schedule" alt={_(ADD_LABELS.scheduledPost)} />
</button>
)}
</span>
</span>
{/* <div class="spacer" /> */}
{uiState === 'loading' ? (
<Loader abrupt />
) : (
@ -1573,13 +1610,7 @@ function Compose({
)}
<label
class={`toolbar-button ${
language !== prevLanguage.current ||
(
autoDetectedLanguages?.length &&
!autoDetectedLanguages.includes(language)
)
? 'highlight'
: ''
highlightLanguageField ? 'highlight' : ''
}`}
>
<span class="icon-text">
@ -1623,6 +1654,47 @@ function Compose({
})}
</select>
</label>{' '}
<label
class={`toolbar-button ${highlightVisibilityField ? 'highlight' : ''}`}
title={_(visibilityText[visibility])}
>
{visibility === 'public' || visibility === 'direct' ? (
<Icon
icon={visibilityIconsMap[visibility]}
alt={_(visibilityText[visibility])}
/>
) : (
<span class="icon-text">{_(visibilityText[visibility])}</span>
)}
<select
name="visibility"
value={visibility}
onChange={(e) => {
setVisibility(e.target.value);
}}
disabled={uiState === 'loading' || !!editStatus}
dir="auto"
>
<option value="public">
<Trans>Public</Trans>
</option>
{(supports('@pleroma/local-visibility-post') ||
supports('@akkoma/local-visibility-post')) && (
<option value="local">
<Trans>Local</Trans>
</option>
)}
<option value="unlisted">
<Trans>Quiet public</Trans>
</option>
<option value="private">
<Trans>Followers</Trans>
</option>
<option value="direct">
<Trans>Private mention</Trans>
</option>
</select>
</label>{' '}
<button type="submit" disabled={uiState === 'loading'}>
{scheduledAt
? t`Schedule`
@ -1642,6 +1714,7 @@ function Compose({
<Modal
onClose={() => {
setShowMentionPicker(false);
focusLastFocusedField();
}}
>
<MentionModal
@ -1667,6 +1740,7 @@ function Compose({
<Modal
onClose={() => {
setShowEmoji2Picker(false);
focusLastFocusedField();
}}
>
<CustomEmojisModal
@ -1690,6 +1764,7 @@ function Compose({
<Modal
onClose={() => {
setShowGIFPicker(false);
focusLastFocusedField();
}}
>
<GIFPickerModal

Wyświetl plik

@ -44,6 +44,7 @@ import { getCurrentAccID, getCurrentAccountID } from '../utils/store-utils';
import supports from '../utils/supports';
import useTruncated from '../utils/useTruncated';
import visibilityIconsMap from '../utils/visibility-icons-map';
import visibilityText from '../utils/visibility-text';
import Avatar from './avatar';
import CustomEmoji from './custom-emoji';
@ -83,14 +84,6 @@ function fetchAccount(id, masto) {
}
const memFetchAccount = pmem(throttle(fetchAccount));
const visibilityText = {
public: msg`Public`,
local: msg`Local`,
unlisted: msg`Unlisted`,
private: msg`Followers only`,
direct: msg`Private mention`,
};
const isIOS =
window.ontouchstart !== undefined &&
/iPad|iPhone|iPod/.test(navigator.userAgent);

Wyświetl plik

@ -100,7 +100,7 @@
--outline-hover-color: rgba(128, 128, 128, 0.7);
--divider-color: rgba(0, 0, 0, 0.1);
--backdrop-color: rgba(0, 0, 0, 0.1);
--backdrop-solid-color: color-mix(in srgb, var(--bg-color) 90%, #000 10%);
--backdrop-solid-color: var(--bg-faded-color);
--backdrop-theme-color: #e5e5e5;
--backdrop-darker-color: rgba(0, 0, 0, 0.25);
--img-bg-color: rgba(128, 128, 128, 0.2);

606
src/locales/en.po wygenerowano
Wyświetl plik

@ -34,7 +34,7 @@ msgstr ""
#: src/components/account-block.jsx:190
#: src/components/account-info.jsx:675
#: src/components/status.jsx:519
#: src/components/status.jsx:512
msgid "Group"
msgstr ""
@ -91,6 +91,9 @@ msgstr "Go to account page"
#: src/components/account-info.jsx:407
#: src/components/account-info.jsx:740
#: src/components/compose.jsx:1691
#: src/pages/settings.jsx:324
#: src/utils/visibility-text.jsx:7
msgid "Followers"
msgstr "Followers"
@ -112,11 +115,11 @@ msgstr ""
#: src/components/media-attachment.jsx:380
#: src/components/media-modal.jsx:363
#: src/components/related-actions.jsx:238
#: src/components/status.jsx:1771
#: src/components/status.jsx:1788
#: src/components/status.jsx:1919
#: src/components/status.jsx:2543
#: src/components/status.jsx:2546
#: src/components/status.jsx:1764
#: src/components/status.jsx:1781
#: src/components/status.jsx:1912
#: src/components/status.jsx:2536
#: src/components/status.jsx:2539
#: src/pages/account-statuses.jsx:539
#: src/pages/accounts.jsx:118
#: src/pages/hashtag.jsx:203
@ -221,7 +224,7 @@ msgid "Original"
msgstr ""
#: src/components/account-info.jsx:943
#: src/components/status.jsx:2328
#: src/components/status.jsx:2321
#: src/pages/catchup.jsx:71
#: src/pages/catchup.jsx:1448
#: src/pages/catchup.jsx:2061
@ -248,7 +251,7 @@ msgstr "View post stats"
#: src/components/account-sheet.jsx:38
#: src/components/add-remove-lists-sheet.jsx:45
#: src/components/compose.jsx:834
#: src/components/compose.jsx:888
#: src/components/custom-emojis-modal.jsx:234
#: src/components/drafts.jsx:57
#: src/components/edit-profile-sheet.jsx:87
@ -268,8 +271,8 @@ msgstr "View post stats"
#: src/components/shortcuts-settings.jsx:230
#: src/components/shortcuts-settings.jsx:583
#: src/components/shortcuts-settings.jsx:783
#: src/components/status.jsx:2742
#: src/components/status.jsx:2954
#: src/components/status.jsx:2735
#: src/components/status.jsx:2947
#: src/components/translated-bio-sheet.jsx:21
#: src/pages/accounts.jsx:45
#: src/pages/catchup.jsx:1584
@ -358,7 +361,7 @@ msgstr "Add to thread"
msgid "Choice {0}"
msgstr "Choice {0}"
#: src/components/compose-poll.jsx:65
#: src/components/compose-poll.jsx:64
#: src/components/media-attachment.jsx:300
#: src/components/shortcuts-settings.jsx:726
#: src/pages/catchup.jsx:1081
@ -366,210 +369,216 @@ msgstr "Choice {0}"
msgid "Remove"
msgstr ""
#: src/components/compose-poll.jsx:93
msgid "Multiple choices"
msgstr "Multiple choices"
#: src/components/compose-poll.jsx:96
msgid "Duration"
msgstr ""
#: src/components/compose-poll.jsx:127
msgid "Remove poll"
msgstr ""
#: src/components/compose.jsx:100
msgid "Take photo or video"
msgstr "Take photo or video"
#: src/components/compose.jsx:101
msgid "Add media"
msgstr "Add media"
#: src/components/compose.jsx:102
msgid "Add custom emoji"
msgstr ""
#: src/components/compose.jsx:103
msgid "Add GIF"
msgstr "Add GIF"
#: src/components/compose.jsx:104
msgid "Add poll"
msgstr ""
#: src/components/compose.jsx:105
msgid "Schedule post"
msgstr "Schedule post"
#: src/components/compose.jsx:359
msgid "You have unsaved changes. Discard this post?"
msgstr "You have unsaved changes. Discard this post?"
#. placeholder {0}: unsupportedFiles.length
#. placeholder {1}: unsupportedFiles[0].name
#. placeholder {2}: lf.format( unsupportedFiles.map((f) => f.name), )
#: src/components/compose.jsx:597
msgid "{0, plural, one {File {1} is not supported.} other {Files {2} are not supported.}}"
msgstr "{0, plural, one {File {1} is not supported.} other {Files {2} are not supported.}}"
#: src/components/compose.jsx:607
#: src/components/compose.jsx:625
#: src/components/compose.jsx:1701
#: src/components/file-picker-input.jsx:38
msgid "{maxMediaAttachments, plural, one {You can only attach up to 1 file.} other {You can only attach up to # files.}}"
msgstr ""
#: src/components/compose.jsx:815
msgid "Pop out"
msgstr "Pop out"
#: src/components/compose.jsx:822
msgid "Minimize"
msgstr "Minimize"
#: src/components/compose.jsx:858
msgid "Looks like you closed the parent window."
msgstr "Looks like you closed the parent window."
#: src/components/compose.jsx:865
msgid "Looks like you already have a compose field open in the parent window and currently publishing. Please wait for it to be done and try again later."
msgstr "Looks like you already have a compose field open in the parent window and currently publishing. Please wait for it to be done and try again later."
#: src/components/compose.jsx:870
msgid "Looks like you already have a compose field open in the parent window. Popping in this window will discard the changes you made in the parent window. Continue?"
msgstr "Looks like you already have a compose field open in the parent window. Popping in this window will discard the changes you made in the parent window. Continue?"
#: src/components/compose.jsx:913
msgid "Pop in"
msgstr "Pop in"
#. placeholder {0}: replyToStatus.account.acct || replyToStatus.account.username
#. placeholder {1}: rtf.format(-replyToStatusMonthsAgo, 'month')
#: src/components/compose.jsx:923
msgid "Replying to @{0}s post (<0>{1}</0>)"
msgstr ""
#. placeholder {0}: replyToStatus.account.acct || replyToStatus.account.username
#: src/components/compose.jsx:933
msgid "Replying to @{0}s post"
msgstr ""
#: src/components/compose.jsx:946
msgid "Editing source post"
msgstr ""
#: src/components/compose.jsx:999
msgid "Poll must have at least 2 options"
msgstr "Poll must have at least 2 options"
#: src/components/compose.jsx:1003
msgid "Some poll choices are empty"
msgstr "Some poll choices are empty"
#: src/components/compose.jsx:1016
msgid "Some media have no descriptions. Continue?"
msgstr "Some media have no descriptions. Continue?"
#: src/components/compose.jsx:1068
msgid "Attachment #{i} failed"
msgstr "Attachment #{i} failed"
#: src/components/compose.jsx:1176
#: src/components/status.jsx:2103
#: src/components/timeline.jsx:1015
msgid "Content warning"
msgstr ""
#: src/components/compose.jsx:1195
msgid "Content warning or sensitive media"
msgstr "Content warning or sensitive media"
#: src/components/compose.jsx:1231
#: src/components/status.jsx:87
#: src/pages/settings.jsx:318
msgid "Public"
msgstr ""
#: src/components/compose.jsx:1236
#: src/components/nav-menu.jsx:349
#: src/components/shortcuts-settings.jsx:165
#: src/components/status.jsx:88
msgid "Local"
msgstr ""
#: src/components/compose.jsx:1240
#: src/components/status.jsx:89
#: src/pages/settings.jsx:321
msgid "Unlisted"
msgstr ""
#: src/components/compose.jsx:1243
#: src/components/status.jsx:90
#: src/pages/settings.jsx:324
msgid "Followers only"
msgstr ""
#: src/components/compose.jsx:1246
#: src/components/status.jsx:91
#: src/components/status.jsx:1983
msgid "Private mention"
msgstr ""
#: src/components/compose.jsx:1256
msgid "Post your reply"
msgstr "Post your reply"
#: src/components/compose.jsx:1258
msgid "Edit your post"
msgstr "Edit your post"
#: src/components/compose.jsx:1259
msgid "What are you doing?"
msgstr "What are you doing?"
#: src/components/compose.jsx:1328
msgid "Mark media as sensitive"
msgstr ""
#: src/components/compose.jsx:1365
msgid "Posting on <0/>"
msgstr "Posting on <0/>"
#: src/components/compose.jsx:1396
#: src/components/compose-poll.jsx:80
#: src/components/compose.jsx:1420
#: src/components/mention-modal.jsx:220
#: src/components/shortcuts-settings.jsx:715
#: src/pages/list.jsx:388
msgid "Add"
msgstr ""
#: src/components/compose.jsx:1628
#: src/components/compose-poll.jsx:96
msgid "Multiple choice"
msgstr "Multiple choice"
#: src/components/compose-poll.jsx:99
msgid "Duration"
msgstr ""
#: src/components/compose-poll.jsx:129
msgid "Remove poll"
msgstr ""
#: src/components/compose.jsx:101
msgid "Take photo or video"
msgstr "Take photo or video"
#: src/components/compose.jsx:102
msgid "Add media"
msgstr "Add media"
#: src/components/compose.jsx:103
msgid "Add custom emoji"
msgstr ""
#: src/components/compose.jsx:104
msgid "Add GIF"
msgstr "Add GIF"
#: src/components/compose.jsx:105
msgid "Add poll"
msgstr ""
#: src/components/compose.jsx:106
msgid "Add content warning"
msgstr "Add content warning"
#: src/components/compose.jsx:107
msgid "Schedule post"
msgstr "Schedule post"
#: src/components/compose.jsx:387
msgid "You have unsaved changes. Discard this post?"
msgstr "You have unsaved changes. Discard this post?"
#. placeholder {0}: unsupportedFiles.length
#. placeholder {1}: unsupportedFiles[0].name
#. placeholder {2}: lf.format( unsupportedFiles.map((f) => f.name), )
#: src/components/compose.jsx:626
msgid "{0, plural, one {File {1} is not supported.} other {Files {2} are not supported.}}"
msgstr "{0, plural, one {File {1} is not supported.} other {Files {2} are not supported.}}"
#: src/components/compose.jsx:636
#: src/components/compose.jsx:654
#: src/components/compose.jsx:1776
#: src/components/file-picker-input.jsx:38
msgid "{maxMediaAttachments, plural, one {You can only attach up to 1 file.} other {You can only attach up to # files.}}"
msgstr ""
#: src/components/compose.jsx:869
msgid "Pop out"
msgstr "Pop out"
#: src/components/compose.jsx:876
msgid "Minimize"
msgstr "Minimize"
#: src/components/compose.jsx:912
msgid "Looks like you closed the parent window."
msgstr "Looks like you closed the parent window."
#: src/components/compose.jsx:919
msgid "Looks like you already have a compose field open in the parent window and currently publishing. Please wait for it to be done and try again later."
msgstr "Looks like you already have a compose field open in the parent window and currently publishing. Please wait for it to be done and try again later."
#: src/components/compose.jsx:924
msgid "Looks like you already have a compose field open in the parent window. Popping in this window will discard the changes you made in the parent window. Continue?"
msgstr "Looks like you already have a compose field open in the parent window. Popping in this window will discard the changes you made in the parent window. Continue?"
#: src/components/compose.jsx:968
msgid "Pop in"
msgstr "Pop in"
#. placeholder {0}: replyToStatus.account.acct || replyToStatus.account.username
#. placeholder {1}: rtf.format(-replyToStatusMonthsAgo, 'month')
#: src/components/compose.jsx:978
msgid "Replying to @{0}s post (<0>{1}</0>)"
msgstr ""
#. placeholder {0}: replyToStatus.account.acct || replyToStatus.account.username
#: src/components/compose.jsx:988
msgid "Replying to @{0}s post"
msgstr ""
#: src/components/compose.jsx:1001
msgid "Editing source post"
msgstr ""
#: src/components/compose.jsx:1065
msgid "Poll must have at least 2 options"
msgstr "Poll must have at least 2 options"
#: src/components/compose.jsx:1069
msgid "Some poll choices are empty"
msgstr "Some poll choices are empty"
#: src/components/compose.jsx:1082
msgid "Some media have no descriptions. Continue?"
msgstr "Some media have no descriptions. Continue?"
#: src/components/compose.jsx:1134
msgid "Attachment #{i} failed"
msgstr "Attachment #{i} failed"
#: src/components/compose.jsx:1243
#: src/components/status.jsx:2096
#: src/components/timeline.jsx:1015
msgid "Content warning"
msgstr ""
#: src/components/compose.jsx:1264
#: src/components/compose.jsx:1397
#: src/components/edit-profile-sheet.jsx:323
#: src/components/private-note-sheet.jsx:94
msgid "Cancel"
msgstr ""
#: src/components/compose.jsx:1272
msgid "Post your reply"
msgstr "Post your reply"
#: src/components/compose.jsx:1274
msgid "Edit your post"
msgstr "Edit your post"
#: src/components/compose.jsx:1276
msgid "Ask a question"
msgstr "Ask a question"
#: src/components/compose.jsx:1277
msgid "What are you doing?"
msgstr "What are you doing?"
#: src/components/compose.jsx:1347
msgid "Mark media as sensitive"
msgstr ""
#: src/components/compose.jsx:1377
msgid "Posting on <0/>"
msgstr "Posting on <0/>"
#: src/components/compose.jsx:1679
#: src/pages/settings.jsx:318
#: src/utils/visibility-text.jsx:4
msgid "Public"
msgstr ""
#: src/components/compose.jsx:1684
#: src/components/nav-menu.jsx:349
#: src/components/shortcuts-settings.jsx:165
#: src/utils/visibility-text.jsx:5
msgid "Local"
msgstr ""
#: src/components/compose.jsx:1688
#: src/pages/settings.jsx:321
#: src/utils/visibility-text.jsx:6
msgid "Quiet public"
msgstr "Quiet public"
#: src/components/compose.jsx:1694
#: src/components/status.jsx:1976
#: src/utils/visibility-text.jsx:8
msgid "Private mention"
msgstr ""
#: src/components/compose.jsx:1700
msgid "Schedule"
msgstr "Schedule"
#: src/components/compose.jsx:1630
#: src/components/compose.jsx:1702
#: src/components/keyboard-shortcuts-help.jsx:155
#: src/components/status.jsx:965
#: src/components/status.jsx:1751
#: src/components/status.jsx:1752
#: src/components/status.jsx:2447
#: src/components/status.jsx:958
#: src/components/status.jsx:1744
#: src/components/status.jsx:1745
#: src/components/status.jsx:2440
msgid "Reply"
msgstr ""
#: src/components/compose.jsx:1632
#: src/components/compose.jsx:1704
msgid "Update"
msgstr "Update"
#: src/components/compose.jsx:1633
#: src/components/compose.jsx:1705
msgctxt "Submit button in composer"
msgid "Post"
msgstr "Post"
#: src/components/compose.jsx:1713
#: src/components/compose.jsx:1788
msgid "Downloading GIF…"
msgstr "Downloading GIF…"
#: src/components/compose.jsx:1741
#: src/components/compose.jsx:1816
msgid "Failed to download GIF"
msgstr "Failed to download GIF"
@ -617,7 +626,7 @@ msgstr ""
#: src/components/drafts.jsx:126
#: src/components/list-add-edit.jsx:188
#: src/components/status.jsx:1368
#: src/components/status.jsx:1361
#: src/pages/filters.jsx:603
#: src/pages/scheduled-posts.jsx:368
msgid "Delete…"
@ -686,11 +695,6 @@ msgstr ""
msgid "Content"
msgstr ""
#: src/components/edit-profile-sheet.jsx:323
#: src/components/private-note-sheet.jsx:94
msgid "Cancel"
msgstr ""
#: src/components/edit-profile-sheet.jsx:326
#: src/components/list-add-edit.jsx:152
#: src/components/shortcuts-settings.jsx:715
@ -914,10 +918,10 @@ msgid "<0>l</0> or <1>f</1>"
msgstr ""
#: src/components/keyboard-shortcuts-help.jsx:176
#: src/components/status.jsx:973
#: src/components/status.jsx:2474
#: src/components/status.jsx:2497
#: src/components/status.jsx:2498
#: src/components/status.jsx:966
#: src/components/status.jsx:2467
#: src/components/status.jsx:2490
#: src/components/status.jsx:2491
msgid "Boost"
msgstr ""
@ -926,9 +930,9 @@ msgid "<0>Shift</0> + <1>b</1>"
msgstr ""
#: src/components/keyboard-shortcuts-help.jsx:184
#: src/components/status.jsx:1036
#: src/components/status.jsx:2522
#: src/components/status.jsx:2523
#: src/components/status.jsx:1029
#: src/components/status.jsx:2515
#: src/components/status.jsx:2516
msgid "Bookmark"
msgstr ""
@ -1011,14 +1015,14 @@ msgid "Media description"
msgstr ""
#: src/components/media-alt-modal.jsx:67
#: src/components/status.jsx:1079
#: src/components/status.jsx:1088
#: src/components/status.jsx:1072
#: src/components/status.jsx:1081
#: src/components/translation-block.jsx:239
msgid "Translate"
msgstr ""
#: src/components/media-alt-modal.jsx:78
#: src/components/status.jsx:1107
#: src/components/status.jsx:1100
msgid "Speak"
msgstr ""
@ -1145,8 +1149,8 @@ msgstr ""
#: src/components/media-post.jsx:133
#: src/components/status-compact.jsx:70
#: src/components/status.jsx:2880
#: src/components/status.jsx:2958
#: src/components/status.jsx:2873
#: src/components/status.jsx:2951
#: src/components/timeline.jsx:1004
#: src/pages/catchup.jsx:75
#: src/pages/catchup.jsx:1880
@ -1467,8 +1471,8 @@ msgid "[Unknown notification type: {type}]"
msgstr ""
#: src/components/notification.jsx:451
#: src/components/status.jsx:1050
#: src/components/status.jsx:1060
#: src/components/status.jsx:1043
#: src/components/status.jsx:1053
msgid "Boosted/Liked by…"
msgstr ""
@ -1494,7 +1498,7 @@ msgid "View #Wrapstodon"
msgstr "View #Wrapstodon"
#: src/components/notification.jsx:801
#: src/components/status.jsx:260
#: src/components/status.jsx:253
msgid "Read more →"
msgstr ""
@ -1558,7 +1562,7 @@ msgid "Ending"
msgstr "Ending"
#: src/components/post-embed-modal.jsx:201
#: src/components/status.jsx:1237
#: src/components/status.jsx:1230
msgid "Embed post"
msgstr ""
@ -1577,7 +1581,7 @@ msgstr ""
#: src/components/post-embed-modal.jsx:232
#: src/components/related-actions.jsx:501
#: src/components/shortcuts-settings.jsx:1059
#: src/components/status.jsx:1202
#: src/components/status.jsx:1195
msgid "Copy"
msgstr ""
@ -1756,23 +1760,23 @@ msgid "Show featured profiles"
msgstr "Show featured profiles"
#: src/components/related-actions.jsx:492
#: src/components/status.jsx:1193
#: src/components/status.jsx:1186
msgid "Link copied"
msgstr ""
#: src/components/related-actions.jsx:495
#: src/components/status.jsx:1196
#: src/components/status.jsx:1189
msgid "Unable to copy link"
msgstr ""
#: src/components/related-actions.jsx:516
#: src/components/shortcuts-settings.jsx:1077
#: src/components/status.jsx:1218
#: src/components/status.jsx:1211
msgid "Sharing doesn't seem to work."
msgstr ""
#: src/components/related-actions.jsx:522
#: src/components/status.jsx:1224
#: src/components/status.jsx:1217
msgid "Share…"
msgstr ""
@ -2110,7 +2114,7 @@ msgid "Move down"
msgstr ""
#: src/components/shortcuts-settings.jsx:379
#: src/components/status.jsx:1330
#: src/components/status.jsx:1323
#: src/pages/list.jsx:195
msgid "Edit"
msgstr ""
@ -2309,268 +2313,268 @@ msgstr ""
msgid "Import/export settings from/to instance server (Very experimental)"
msgstr ""
#: src/components/status.jsx:543
#: src/components/status.jsx:536
msgid "<0/> <1>boosted</1>"
msgstr "<0/> <1>boosted</1>"
#: src/components/status.jsx:646
#: src/components/status.jsx:639
msgid "Sorry, your current logged-in instance can't interact with this post from another instance."
msgstr ""
#. placeholder {0}: username || acct
#: src/components/status.jsx:800
#: src/components/status.jsx:793
msgid "Unliked @{0}'s post"
msgstr ""
#. placeholder {0}: username || acct
#: src/components/status.jsx:801
#: src/components/status.jsx:794
msgid "Liked @{0}'s post"
msgstr "Liked @{0}'s post"
#. placeholder {0}: username || acct
#: src/components/status.jsx:840
#: src/components/status.jsx:833
msgid "Unbookmarked @{0}'s post"
msgstr "Unbookmarked @{0}'s post"
#. placeholder {0}: username || acct
#: src/components/status.jsx:841
#: src/components/status.jsx:834
msgid "Bookmarked @{0}'s post"
msgstr "Bookmarked @{0}'s post"
#: src/components/status.jsx:942
#: src/components/status.jsx:935
msgid "Some media have no descriptions."
msgstr ""
#. placeholder {0}: rtf.format(-statusMonthsAgo, 'month')
#: src/components/status.jsx:949
#: src/components/status.jsx:942
msgid "Old post (<0>{0}</0>)"
msgstr ""
#: src/components/status.jsx:973
#: src/components/status.jsx:1013
#: src/components/status.jsx:2474
#: src/components/status.jsx:2497
#: src/components/status.jsx:966
#: src/components/status.jsx:1006
#: src/components/status.jsx:2467
#: src/components/status.jsx:2490
msgid "Unboost"
msgstr ""
#: src/components/status.jsx:989
#: src/components/status.jsx:2489
#: src/components/status.jsx:982
#: src/components/status.jsx:2482
msgid "Quote"
msgstr ""
#. placeholder {0}: username || acct
#: src/components/status.jsx:1001
#: src/components/status.jsx:1467
#: src/components/status.jsx:994
#: src/components/status.jsx:1460
msgid "Unboosted @{0}'s post"
msgstr "Unboosted @{0}'s post"
#. placeholder {0}: username || acct
#: src/components/status.jsx:1002
#: src/components/status.jsx:1468
#: src/components/status.jsx:995
#: src/components/status.jsx:1461
msgid "Boosted @{0}'s post"
msgstr "Boosted @{0}'s post"
#: src/components/status.jsx:1014
#: src/components/status.jsx:1007
msgid "Boost…"
msgstr ""
#: src/components/status.jsx:1026
#: src/components/status.jsx:1761
#: src/components/status.jsx:2510
#: src/components/status.jsx:1019
#: src/components/status.jsx:1754
#: src/components/status.jsx:2503
msgid "Unlike"
msgstr ""
#: src/components/status.jsx:1027
#: src/components/status.jsx:1761
#: src/components/status.jsx:1762
#: src/components/status.jsx:2510
#: src/components/status.jsx:2511
#: src/components/status.jsx:1020
#: src/components/status.jsx:1754
#: src/components/status.jsx:1755
#: src/components/status.jsx:2503
#: src/components/status.jsx:2504
msgid "Like"
msgstr ""
#: src/components/status.jsx:1036
#: src/components/status.jsx:2522
#: src/components/status.jsx:1029
#: src/components/status.jsx:2515
msgid "Unbookmark"
msgstr ""
#: src/components/status.jsx:1119
#: src/components/status.jsx:1112
msgid "Post text copied"
msgstr "Post text copied"
#: src/components/status.jsx:1122
#: src/components/status.jsx:1115
msgid "Unable to copy post text"
msgstr "Unable to copy post text"
#: src/components/status.jsx:1128
#: src/components/status.jsx:1121
msgid "Copy post text"
msgstr "Copy post text"
#. placeholder {0}: username || acct
#: src/components/status.jsx:1146
#: src/components/status.jsx:1139
msgid "View post by <0>@{0}</0>"
msgstr ""
#: src/components/status.jsx:1167
#: src/components/status.jsx:1160
msgid "Show Edit History"
msgstr ""
#: src/components/status.jsx:1170
#: src/components/status.jsx:1163
msgid "Edited: {editedDateText}"
msgstr ""
#: src/components/status.jsx:1251
#: src/components/status.jsx:1244
msgid "Conversation unmuted"
msgstr ""
#: src/components/status.jsx:1251
#: src/components/status.jsx:1244
msgid "Conversation muted"
msgstr ""
#: src/components/status.jsx:1257
#: src/components/status.jsx:1250
msgid "Unable to unmute conversation"
msgstr ""
#: src/components/status.jsx:1258
#: src/components/status.jsx:1251
msgid "Unable to mute conversation"
msgstr ""
#: src/components/status.jsx:1267
#: src/components/status.jsx:1260
msgid "Unmute conversation"
msgstr ""
#: src/components/status.jsx:1274
#: src/components/status.jsx:1267
msgid "Mute conversation"
msgstr ""
#: src/components/status.jsx:1290
#: src/components/status.jsx:1283
msgid "Post unpinned from profile"
msgstr ""
#: src/components/status.jsx:1291
#: src/components/status.jsx:1284
msgid "Post pinned to profile"
msgstr ""
#: src/components/status.jsx:1296
#: src/components/status.jsx:1289
msgid "Unable to unpin post"
msgstr ""
#: src/components/status.jsx:1296
#: src/components/status.jsx:1289
msgid "Unable to pin post"
msgstr ""
#: src/components/status.jsx:1305
#: src/components/status.jsx:1298
msgid "Unpin from profile"
msgstr ""
#: src/components/status.jsx:1312
#: src/components/status.jsx:1305
msgid "Pin to profile"
msgstr ""
#: src/components/status.jsx:1341
#: src/components/status.jsx:1334
msgid "Delete this post?"
msgstr ""
#: src/components/status.jsx:1357
#: src/components/status.jsx:1350
msgid "Post deleted"
msgstr ""
#: src/components/status.jsx:1360
#: src/components/status.jsx:1353
msgid "Unable to delete post"
msgstr ""
#: src/components/status.jsx:1388
#: src/components/status.jsx:1381
msgid "Report post…"
msgstr ""
#: src/components/status.jsx:1762
#: src/components/status.jsx:1798
#: src/components/status.jsx:2511
#: src/components/status.jsx:1755
#: src/components/status.jsx:1791
#: src/components/status.jsx:2504
msgid "Liked"
msgstr ""
#: src/components/status.jsx:1795
#: src/components/status.jsx:2498
#: src/components/status.jsx:1788
#: src/components/status.jsx:2491
msgid "Boosted"
msgstr ""
#: src/components/status.jsx:1805
#: src/components/status.jsx:2523
#: src/components/status.jsx:1798
#: src/components/status.jsx:2516
msgid "Bookmarked"
msgstr ""
#: src/components/status.jsx:1809
#: src/components/status.jsx:1802
msgid "Pinned"
msgstr ""
#: src/components/status.jsx:1861
#: src/components/status.jsx:2336
#: src/components/status.jsx:1854
#: src/components/status.jsx:2329
msgid "Deleted"
msgstr ""
#: src/components/status.jsx:1902
#: src/components/status.jsx:1895
msgid "{repliesCount, plural, one {# reply} other {# replies}}"
msgstr ""
#: src/components/status.jsx:2066
#: src/components/status.jsx:2128
#: src/components/status.jsx:2232
#: src/components/status.jsx:2059
#: src/components/status.jsx:2121
#: src/components/status.jsx:2225
msgid "Show less"
msgstr ""
#: src/components/status.jsx:2066
#: src/components/status.jsx:2128
#: src/components/status.jsx:2059
#: src/components/status.jsx:2121
msgid "Show content"
msgstr ""
#. placeholder {0}: filterInfo.titlesStr
#. placeholder {0}: filterInfo?.titlesStr
#: src/components/status.jsx:2228
#: src/components/status.jsx:2221
#: src/pages/catchup.jsx:1879
msgid "Filtered: {0}"
msgstr "Filtered: {0}"
#: src/components/status.jsx:2232
#: src/components/status.jsx:2225
msgid "Show media"
msgstr ""
#: src/components/status.jsx:2371
#: src/components/status.jsx:2364
msgid "Edited"
msgstr ""
#: src/components/status.jsx:2448
#: src/components/status.jsx:2441
msgid "Comments"
msgstr ""
#: src/components/status.jsx:2633
#: src/components/status.jsx:2626
msgid "Post hidden by your filters"
msgstr "Post hidden by your filters"
#: src/components/status.jsx:2634
#: src/components/status.jsx:2627
msgid "Post pending"
msgstr "Post pending"
#: src/components/status.jsx:2635
#: src/components/status.jsx:2636
#: src/components/status.jsx:2637
#: src/components/status.jsx:2638
#: src/components/status.jsx:2628
#: src/components/status.jsx:2629
#: src/components/status.jsx:2630
#: src/components/status.jsx:2631
msgid "Post unavailable"
msgstr "Post unavailable"
#: src/components/status.jsx:2747
#: src/components/status.jsx:2740
msgid "Edit History"
msgstr ""
#: src/components/status.jsx:2751
#: src/components/status.jsx:2744
msgid "Failed to load history"
msgstr ""
#: src/components/status.jsx:2756
#: src/components/status.jsx:2749
#: src/pages/annual-report.jsx:45
msgid "Loading…"
msgstr ""
#. [Name] [Visibility icon] boosted
#: src/components/status.jsx:2888
#: src/components/status.jsx:2881
msgid "<0/> <1/> boosted"
msgstr "<0/> <1/> boosted"

Wyświetl plik

@ -873,7 +873,7 @@ export default function Sandbox() {
checked={toggleState.visibility === 'unlisted'}
onChange={() => updateToggles({ visibility: 'unlisted' })}
/>
<span>Unlisted</span>
<span>Quiet public</span>
</label>
</li>
<li>

Wyświetl plik

@ -318,10 +318,10 @@ function Settings({ onClose }) {
<Trans>Public</Trans>
</option>
<option value="unlisted">
<Trans>Unlisted</Trans>
<Trans>Quiet public</Trans>
</option>
<option value="private">
<Trans>Followers only</Trans>
<Trans>Followers</Trans>
</option>
</select>
</div>

Wyświetl plik

@ -0,0 +1,11 @@
import { msg } from '@lingui/core/macro';
const visibilityText = {
public: msg`Public`,
local: msg`Local`,
unlisted: msg`Quiet public`,
private: msg`Followers`,
direct: msg`Private mention`,
};
export default visibilityText;