Merge pull request #3272 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 1c3e7545cb6137025a6efd208d195352c54ffda8
This commit is contained in:
commit
2759bafe09
@ -47,7 +47,7 @@ export const AltTextBadge: React.FC<{ description: string }> = ({
|
|||||||
rootClose
|
rootClose
|
||||||
onHide={handleClose}
|
onHide={handleClose}
|
||||||
show={open}
|
show={open}
|
||||||
target={anchorRef.current}
|
target={anchorRef}
|
||||||
placement='top-end'
|
placement='top-end'
|
||||||
flip
|
flip
|
||||||
offset={offset}
|
offset={offset}
|
||||||
|
|||||||
@ -76,6 +76,11 @@ export const Carousel = <
|
|||||||
// Handle slide change
|
// Handle slide change
|
||||||
const [slideIndex, setSlideIndex] = useState(0);
|
const [slideIndex, setSlideIndex] = useState(0);
|
||||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
|
// Handle slide heights
|
||||||
|
const [currentSlideHeight, setCurrentSlideHeight] = useState(
|
||||||
|
() => wrapperRef.current?.scrollHeight ?? 0,
|
||||||
|
);
|
||||||
|
const previousSlideHeight = usePrevious(currentSlideHeight);
|
||||||
const handleSlideChange = useCallback(
|
const handleSlideChange = useCallback(
|
||||||
(direction: number) => {
|
(direction: number) => {
|
||||||
setSlideIndex((prev) => {
|
setSlideIndex((prev) => {
|
||||||
@ -101,16 +106,11 @@ export const Carousel = <
|
|||||||
[items.length, onChangeSlide],
|
[items.length, onChangeSlide],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle slide heights
|
const observerRef = useRef<ResizeObserver | null>(null);
|
||||||
const [currentSlideHeight, setCurrentSlideHeight] = useState(
|
observerRef.current ??= new ResizeObserver(() => {
|
||||||
wrapperRef.current?.scrollHeight ?? 0,
|
handleSlideChange(0);
|
||||||
);
|
});
|
||||||
const previousSlideHeight = usePrevious(currentSlideHeight);
|
|
||||||
const observerRef = useRef<ResizeObserver>(
|
|
||||||
new ResizeObserver(() => {
|
|
||||||
handleSlideChange(0);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const wrapperStyles = useSpring({
|
const wrapperStyles = useSpring({
|
||||||
x: `-${slideIndex * 100}%`,
|
x: `-${slideIndex * 100}%`,
|
||||||
height: currentSlideHeight,
|
height: currentSlideHeight,
|
||||||
@ -200,7 +200,7 @@ export const Carousel = <
|
|||||||
};
|
};
|
||||||
|
|
||||||
type CarouselSlideWrapperProps<SlideProps extends CarouselSlideProps> = {
|
type CarouselSlideWrapperProps<SlideProps extends CarouselSlideProps> = {
|
||||||
observer: ResizeObserver;
|
observer: ResizeObserver | null;
|
||||||
className: string;
|
className: string;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
item: SlideProps;
|
item: SlideProps;
|
||||||
@ -217,7 +217,7 @@ const CarouselSlideWrapper = <SlideProps extends CarouselSlideProps>({
|
|||||||
}: CarouselSlideWrapperProps<SlideProps>) => {
|
}: CarouselSlideWrapperProps<SlideProps>) => {
|
||||||
const handleRef = useCallback(
|
const handleRef = useCallback(
|
||||||
(instance: HTMLDivElement | null) => {
|
(instance: HTMLDivElement | null) => {
|
||||||
if (instance) {
|
if (observer && instance) {
|
||||||
observer.observe(instance);
|
observer.observe(instance);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useState, useEffect, useRef } from 'react';
|
import { useCallback, useState, useRef } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
@ -12,11 +12,15 @@ export const ColumnSearchHeader: React.FC<{
|
|||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
// Reset the component when it turns from active to inactive.
|
||||||
|
// [More on this pattern](https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes)
|
||||||
|
const [previousActive, setPreviousActive] = useState(active);
|
||||||
|
if (active !== previousActive) {
|
||||||
|
setPreviousActive(active);
|
||||||
if (!active) {
|
if (!active) {
|
||||||
setValue('');
|
setValue('');
|
||||||
}
|
}
|
||||||
}, [active]);
|
}
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
|
({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
|||||||
@ -109,7 +109,7 @@ export const Dropdown: FC<
|
|||||||
placement='bottom-start'
|
placement='bottom-start'
|
||||||
onHide={handleClose}
|
onHide={handleClose}
|
||||||
flip
|
flip
|
||||||
target={buttonRef.current}
|
target={buttonRef}
|
||||||
popperConfig={{
|
popperConfig={{
|
||||||
strategy: 'fixed',
|
strategy: 'fixed',
|
||||||
modifiers: [matchWidth],
|
modifiers: [matchWidth],
|
||||||
|
|||||||
@ -42,16 +42,10 @@ import { IconButton } from './icon_button';
|
|||||||
|
|
||||||
let id = 0;
|
let id = 0;
|
||||||
|
|
||||||
export interface RenderItemFnHandlers {
|
|
||||||
onClick: React.MouseEventHandler;
|
|
||||||
onKeyUp: React.KeyboardEventHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RenderItemFn<Item = MenuItem> = (
|
export type RenderItemFn<Item = MenuItem> = (
|
||||||
item: Item,
|
item: Item,
|
||||||
index: number,
|
index: number,
|
||||||
handlers: RenderItemFnHandlers,
|
onClick: React.MouseEventHandler,
|
||||||
focusRefCallback?: (c: HTMLAnchorElement | HTMLButtonElement | null) => void,
|
|
||||||
) => React.ReactNode;
|
) => React.ReactNode;
|
||||||
|
|
||||||
type ItemClickFn<Item = MenuItem> = (item: Item, index: number) => void;
|
type ItemClickFn<Item = MenuItem> = (item: Item, index: number) => void;
|
||||||
@ -101,7 +95,6 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
onItemClick,
|
onItemClick,
|
||||||
}: DropdownMenuProps<Item>) => {
|
}: DropdownMenuProps<Item>) => {
|
||||||
const nodeRef = useRef<HTMLDivElement>(null);
|
const nodeRef = useRef<HTMLDivElement>(null);
|
||||||
const focusedItemRef = useRef<HTMLElement | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleDocumentClick = (e: MouseEvent) => {
|
const handleDocumentClick = (e: MouseEvent) => {
|
||||||
@ -163,8 +156,11 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
document.addEventListener('click', handleDocumentClick, { capture: true });
|
document.addEventListener('click', handleDocumentClick, { capture: true });
|
||||||
document.addEventListener('keydown', handleKeyDown, { capture: true });
|
document.addEventListener('keydown', handleKeyDown, { capture: true });
|
||||||
|
|
||||||
if (focusedItemRef.current && openedViaKeyboard) {
|
if (openedViaKeyboard) {
|
||||||
focusedItemRef.current.focus({ preventScroll: true });
|
const firstMenuItem = nodeRef.current?.querySelector<
|
||||||
|
HTMLAnchorElement | HTMLButtonElement
|
||||||
|
>('li:first-child > :is(a, button)');
|
||||||
|
firstMenuItem?.focus({ preventScroll: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -175,13 +171,6 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
};
|
};
|
||||||
}, [onClose, openedViaKeyboard]);
|
}, [onClose, openedViaKeyboard]);
|
||||||
|
|
||||||
const handleFocusedItemRef = useCallback(
|
|
||||||
(c: HTMLAnchorElement | HTMLButtonElement | null) => {
|
|
||||||
focusedItemRef.current = c as HTMLElement;
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleItemClick = useCallback(
|
const handleItemClick = useCallback(
|
||||||
(e: React.MouseEvent | React.KeyboardEvent) => {
|
(e: React.MouseEvent | React.KeyboardEvent) => {
|
||||||
const i = Number(e.currentTarget.getAttribute('data-index'));
|
const i = Number(e.currentTarget.getAttribute('data-index'));
|
||||||
@ -207,15 +196,6 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
[onClose, onItemClick, items],
|
[onClose, onItemClick, items],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleItemKeyUp = useCallback(
|
|
||||||
(e: React.KeyboardEvent) => {
|
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
|
||||||
handleItemClick(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[handleItemClick],
|
|
||||||
);
|
|
||||||
|
|
||||||
const nativeRenderItem = (option: Item, i: number) => {
|
const nativeRenderItem = (option: Item, i: number) => {
|
||||||
if (!isMenuItem(option)) {
|
if (!isMenuItem(option)) {
|
||||||
return null;
|
return null;
|
||||||
@ -232,9 +212,7 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
if (isActionItem(option)) {
|
if (isActionItem(option)) {
|
||||||
element = (
|
element = (
|
||||||
<button
|
<button
|
||||||
ref={i === 0 ? handleFocusedItemRef : undefined}
|
|
||||||
onClick={handleItemClick}
|
onClick={handleItemClick}
|
||||||
onKeyUp={handleItemKeyUp}
|
|
||||||
data-index={i}
|
data-index={i}
|
||||||
aria-disabled={disabled}
|
aria-disabled={disabled}
|
||||||
type='button'
|
type='button'
|
||||||
@ -249,9 +227,7 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
target={option.target ?? '_target'}
|
target={option.target ?? '_target'}
|
||||||
data-method={option.method}
|
data-method={option.method}
|
||||||
rel='noopener'
|
rel='noopener'
|
||||||
ref={i === 0 ? handleFocusedItemRef : undefined}
|
|
||||||
onClick={handleItemClick}
|
onClick={handleItemClick}
|
||||||
onKeyUp={handleItemKeyUp}
|
|
||||||
data-index={i}
|
data-index={i}
|
||||||
>
|
>
|
||||||
<DropdownMenuItemContent item={option} />
|
<DropdownMenuItemContent item={option} />
|
||||||
@ -259,13 +235,7 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
element = (
|
element = (
|
||||||
<Link
|
<Link to={option.to} onClick={handleItemClick} data-index={i}>
|
||||||
to={option.to}
|
|
||||||
ref={i === 0 ? handleFocusedItemRef : undefined}
|
|
||||||
onClick={handleItemClick}
|
|
||||||
onKeyUp={handleItemKeyUp}
|
|
||||||
data-index={i}
|
|
||||||
>
|
|
||||||
<DropdownMenuItemContent item={option} />
|
<DropdownMenuItemContent item={option} />
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@ -308,15 +278,7 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{items.map((option, i) =>
|
{items.map((option, i) =>
|
||||||
renderItemMethod(
|
renderItemMethod(option, i, handleItemClick),
|
||||||
option,
|
|
||||||
i,
|
|
||||||
{
|
|
||||||
onClick: handleItemClick,
|
|
||||||
onKeyUp: handleItemKeyUp,
|
|
||||||
},
|
|
||||||
i === 0 ? handleFocusedItemRef : undefined,
|
|
||||||
),
|
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
@ -400,7 +362,7 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
}, [dispatch, currentId]);
|
}, [dispatch, currentId]);
|
||||||
|
|
||||||
const handleItemClick = useCallback(
|
const handleItemClick = useCallback(
|
||||||
(e: React.MouseEvent | React.KeyboardEvent) => {
|
(e: React.MouseEvent) => {
|
||||||
const i = Number(e.currentTarget.getAttribute('data-index'));
|
const i = Number(e.currentTarget.getAttribute('data-index'));
|
||||||
const item = items?.[i];
|
const item = items?.[i];
|
||||||
|
|
||||||
@ -421,10 +383,20 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
[handleClose, onItemClick, items],
|
[handleClose, onItemClick, items],
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleDropdown = useCallback(
|
const isKeypressRef = useRef(false);
|
||||||
(e: React.MouseEvent | React.KeyboardEvent) => {
|
|
||||||
const { type } = e;
|
|
||||||
|
|
||||||
|
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
||||||
|
if (e.key === ' ' || e.key === 'Enter') {
|
||||||
|
isKeypressRef.current = true;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const unsetIsKeypress = useCallback(() => {
|
||||||
|
isKeypressRef.current = false;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggleDropdown = useCallback(
|
||||||
|
(e: React.MouseEvent) => {
|
||||||
if (open) {
|
if (open) {
|
||||||
handleClose();
|
handleClose();
|
||||||
} else {
|
} else {
|
||||||
@ -451,10 +423,11 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
dispatch(
|
dispatch(
|
||||||
openDropdownMenu({
|
openDropdownMenu({
|
||||||
id: currentId,
|
id: currentId,
|
||||||
keyboard: type !== 'click',
|
keyboard: isKeypressRef.current,
|
||||||
scrollKey,
|
scrollKey,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
isKeypressRef.current = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -485,6 +458,9 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
const buttonProps = {
|
const buttonProps = {
|
||||||
disabled,
|
disabled,
|
||||||
onClick: toggleDropdown,
|
onClick: toggleDropdown,
|
||||||
|
onKeyDown: handleKeyDown,
|
||||||
|
onKeyUp: unsetIsKeypress,
|
||||||
|
onBlur: unsetIsKeypress,
|
||||||
'aria-expanded': open,
|
'aria-expanded': open,
|
||||||
'aria-controls': menuId,
|
'aria-controls': menuId,
|
||||||
ref: buttonRef,
|
ref: buttonRef,
|
||||||
|
|||||||
@ -58,17 +58,7 @@ export const EditedTimestamp: React.FC<{
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
(
|
(item: HistoryItem, index: number, onClick: React.MouseEventHandler) => {
|
||||||
item: HistoryItem,
|
|
||||||
index: number,
|
|
||||||
{
|
|
||||||
onClick,
|
|
||||||
onKeyUp,
|
|
||||||
}: {
|
|
||||||
onClick: React.MouseEventHandler;
|
|
||||||
onKeyUp: React.KeyboardEventHandler;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
const formattedDate = (
|
const formattedDate = (
|
||||||
<RelativeTimestamp
|
<RelativeTimestamp
|
||||||
timestamp={item.get('created_at') as string}
|
timestamp={item.get('created_at') as string}
|
||||||
@ -98,12 +88,7 @@ export const EditedTimestamp: React.FC<{
|
|||||||
className='dropdown-menu__item edited-timestamp__history__item'
|
className='dropdown-menu__item edited-timestamp__history__item'
|
||||||
key={item.get('created_at') as string}
|
key={item.get('created_at') as string}
|
||||||
>
|
>
|
||||||
<button
|
<button data-index={index} onClick={onClick} type='button'>
|
||||||
data-index={index}
|
|
||||||
onClick={onClick}
|
|
||||||
onKeyUp={onKeyUp}
|
|
||||||
type='button'
|
|
||||||
>
|
|
||||||
{label}
|
{label}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@ -27,22 +27,23 @@ export const ExitAnimationWrapper: React.FC<{
|
|||||||
*/
|
*/
|
||||||
children: (delayedIsActive: boolean) => React.ReactNode;
|
children: (delayedIsActive: boolean) => React.ReactNode;
|
||||||
}> = ({ isActive = false, delayMs = 500, withEntryDelay, children }) => {
|
}> = ({ isActive = false, delayMs = 500, withEntryDelay, children }) => {
|
||||||
const [delayedIsActive, setDelayedIsActive] = useState(false);
|
const [delayedIsActive, setDelayedIsActive] = useState(
|
||||||
|
isActive && !withEntryDelay,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isActive && !withEntryDelay) {
|
const withDelay = !isActive || withEntryDelay;
|
||||||
setDelayedIsActive(true);
|
|
||||||
|
|
||||||
return () => '';
|
const timeout = setTimeout(
|
||||||
} else {
|
() => {
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
setDelayedIsActive(isActive);
|
setDelayedIsActive(isActive);
|
||||||
}, delayMs);
|
},
|
||||||
|
withDelay ? delayMs : 0,
|
||||||
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}, [isActive, delayMs, withEntryDelay]);
|
}, [isActive, delayMs, withEntryDelay]);
|
||||||
|
|
||||||
if (!isActive && !delayedIsActive) {
|
if (!isActive && !delayedIsActive) {
|
||||||
|
|||||||
@ -27,7 +27,6 @@ export const HoverCardController: React.FC = () => {
|
|||||||
const [setLeaveTimeout, cancelLeaveTimeout] = useTimeout();
|
const [setLeaveTimeout, cancelLeaveTimeout] = useTimeout();
|
||||||
const [setEnterTimeout, cancelEnterTimeout, delayEnterTimeout] = useTimeout();
|
const [setEnterTimeout, cancelEnterTimeout, delayEnterTimeout] = useTimeout();
|
||||||
const [setScrollTimeout] = useTimeout();
|
const [setScrollTimeout] = useTimeout();
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
cancelEnterTimeout();
|
cancelEnterTimeout();
|
||||||
@ -36,9 +35,12 @@ export const HoverCardController: React.FC = () => {
|
|||||||
setAnchor(null);
|
setAnchor(null);
|
||||||
}, [cancelEnterTimeout, cancelLeaveTimeout, setOpen, setAnchor]);
|
}, [cancelEnterTimeout, cancelLeaveTimeout, setOpen, setAnchor]);
|
||||||
|
|
||||||
useEffect(() => {
|
const location = useLocation();
|
||||||
|
const [previousLocation, setPreviousLocation] = useState(location);
|
||||||
|
if (location !== previousLocation) {
|
||||||
|
setPreviousLocation(location);
|
||||||
handleClose();
|
handleClose();
|
||||||
}, [handleClose, location]);
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isScrolling = false;
|
let isScrolling = false;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useCallback, forwardRef } from 'react';
|
import { useCallback, forwardRef } from 'react';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
@ -59,23 +59,6 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(
|
|||||||
},
|
},
|
||||||
buttonRef,
|
buttonRef,
|
||||||
) => {
|
) => {
|
||||||
const [activate, setActivate] = useState(false);
|
|
||||||
const [deactivate, setDeactivate] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!animate) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activate && !active) {
|
|
||||||
setActivate(false);
|
|
||||||
setDeactivate(true);
|
|
||||||
} else if (!activate && active) {
|
|
||||||
setActivate(true);
|
|
||||||
setDeactivate(false);
|
|
||||||
}
|
|
||||||
}, [setActivate, setDeactivate, animate, active, activate]);
|
|
||||||
|
|
||||||
const handleClick: React.MouseEventHandler<HTMLButtonElement> = useCallback(
|
const handleClick: React.MouseEventHandler<HTMLButtonElement> = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -116,8 +99,8 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(
|
|||||||
active,
|
active,
|
||||||
disabled,
|
disabled,
|
||||||
inverted,
|
inverted,
|
||||||
activate,
|
activate: animate && active,
|
||||||
deactivate,
|
deactivate: animate && !active,
|
||||||
overlayed: overlay,
|
overlayed: overlay,
|
||||||
'icon-button--with-counter': typeof counter !== 'undefined',
|
'icon-button--with-counter': typeof counter !== 'undefined',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -35,6 +35,9 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isPollExpired = (expiresAt: Model.Poll['expires_at']) =>
|
||||||
|
new Date(expiresAt).getTime() < Date.now();
|
||||||
|
|
||||||
interface PollProps {
|
interface PollProps {
|
||||||
pollId: string;
|
pollId: string;
|
||||||
status: Status;
|
status: Status;
|
||||||
@ -58,8 +61,7 @@ export const Poll: React.FC<PollProps> = ({ pollId, disabled, status }) => {
|
|||||||
if (!poll) {
|
if (!poll) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const expiresAt = poll.expires_at;
|
return poll.expired || isPollExpired(poll.expires_at);
|
||||||
return poll.expired || new Date(expiresAt).getTime() < Date.now();
|
|
||||||
}, [poll]);
|
}, [poll]);
|
||||||
const timeRemaining = useMemo(() => {
|
const timeRemaining = useMemo(() => {
|
||||||
if (!poll) {
|
if (!poll) {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import type { Status } from '@/flavours/glitch/models/status';
|
|||||||
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
|
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
|
||||||
import type { SomeRequired } from '@/flavours/glitch/utils/types';
|
import type { SomeRequired } from '@/flavours/glitch/utils/types';
|
||||||
|
|
||||||
import type { RenderItemFn, RenderItemFnHandlers } from '../dropdown_menu';
|
import type { RenderItemFn } from '../dropdown_menu';
|
||||||
import { Dropdown, DropdownMenuItemContent } from '../dropdown_menu';
|
import { Dropdown, DropdownMenuItemContent } from '../dropdown_menu';
|
||||||
import { IconButton } from '../icon_button';
|
import { IconButton } from '../icon_button';
|
||||||
|
|
||||||
@ -74,18 +74,12 @@ const StandaloneBoostButton: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderMenuItem: RenderItemFn<ActionMenuItem> = (
|
const renderMenuItem: RenderItemFn<ActionMenuItem> = (item, index, onClick) => (
|
||||||
item,
|
|
||||||
index,
|
|
||||||
handlers,
|
|
||||||
focusRefCallback,
|
|
||||||
) => (
|
|
||||||
<ReblogMenuItem
|
<ReblogMenuItem
|
||||||
index={index}
|
index={index}
|
||||||
item={item}
|
item={item}
|
||||||
handlers={handlers}
|
onClick={onClick}
|
||||||
key={`${item.text}-${index}`}
|
key={`${item.text}-${index}`}
|
||||||
focusRefCallback={focusRefCallback}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -118,6 +112,18 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
const statusId = status.get('id') as string;
|
const statusId = status.get('id') as string;
|
||||||
const wasBoosted = !!status.get('reblogged');
|
const wasBoosted = !!status.get('reblogged');
|
||||||
|
|
||||||
|
let count: number | undefined;
|
||||||
|
if (counters) {
|
||||||
|
count = 0;
|
||||||
|
// Ensure count is a valid integer.
|
||||||
|
if (Number.isInteger(status.get('reblogs_count'))) {
|
||||||
|
count += status.get('reblogs_count') as number;
|
||||||
|
}
|
||||||
|
if (Number.isInteger(status.get('quotes_count'))) {
|
||||||
|
count += status.get('quotes_count') as number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const showLoginPrompt = useCallback(() => {
|
const showLoginPrompt = useCallback(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
openModal({
|
openModal({
|
||||||
@ -193,12 +199,7 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
)}
|
)}
|
||||||
icon='retweet'
|
icon='retweet'
|
||||||
iconComponent={boostIcon}
|
iconComponent={boostIcon}
|
||||||
counter={
|
counter={count}
|
||||||
counters
|
|
||||||
? (status.get('reblogs_count') as number) +
|
|
||||||
(status.get('quotes_count') as number)
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
active={isReblogged}
|
active={isReblogged}
|
||||||
/>
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
@ -208,16 +209,10 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
interface ReblogMenuItemProps {
|
interface ReblogMenuItemProps {
|
||||||
item: ActionMenuItem;
|
item: ActionMenuItem;
|
||||||
index: number;
|
index: number;
|
||||||
handlers: RenderItemFnHandlers;
|
onClick: React.MouseEventHandler;
|
||||||
focusRefCallback?: (c: HTMLAnchorElement | HTMLButtonElement | null) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReblogMenuItem: FC<ReblogMenuItemProps> = ({
|
const ReblogMenuItem: FC<ReblogMenuItemProps> = ({ index, item, onClick }) => {
|
||||||
index,
|
|
||||||
item,
|
|
||||||
handlers,
|
|
||||||
focusRefCallback,
|
|
||||||
}) => {
|
|
||||||
const { text, highlighted, disabled } = item;
|
const { text, highlighted, disabled } = item;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -228,8 +223,7 @@ const ReblogMenuItem: FC<ReblogMenuItemProps> = ({
|
|||||||
key={`${text}-${index}`}
|
key={`${text}-${index}`}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
{...handlers}
|
onClick={onClick}
|
||||||
ref={focusRefCallback}
|
|
||||||
aria-disabled={disabled}
|
aria-disabled={disabled}
|
||||||
data-index={index}
|
data-index={index}
|
||||||
type='button'
|
type='button'
|
||||||
|
|||||||
@ -44,6 +44,7 @@ export const RemoveQuoteHint: React.FC<{
|
|||||||
|
|
||||||
if (!firstHintId) {
|
if (!firstHintId) {
|
||||||
firstHintId = uniqueId;
|
firstHintId = uniqueId;
|
||||||
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
setIsOnlyHint(true);
|
setIsOnlyHint(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,8 +65,8 @@ export const RemoveQuoteHint: React.FC<{
|
|||||||
flip
|
flip
|
||||||
offset={[12, 10]}
|
offset={[12, 10]}
|
||||||
placement='bottom-end'
|
placement='bottom-end'
|
||||||
target={anchorRef.current}
|
target={anchorRef}
|
||||||
container={anchorRef.current}
|
container={anchorRef}
|
||||||
>
|
>
|
||||||
{({ props, placement }) => (
|
{({ props, placement }) => (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -86,6 +86,7 @@ export const ScrollContext: React.FC<ScrollContextProps> = ({
|
|||||||
) =>
|
) =>
|
||||||
// Hack to allow accessing scrollBehavior._stateStorage
|
// Hack to allow accessing scrollBehavior._stateStorage
|
||||||
shouldUpdateScroll.call(
|
shouldUpdateScroll.call(
|
||||||
|
// eslint-disable-next-line react-hooks/immutability
|
||||||
scrollBehavior,
|
scrollBehavior,
|
||||||
prevLocationContext,
|
prevLocationContext,
|
||||||
locationContext,
|
locationContext,
|
||||||
|
|||||||
@ -101,16 +101,17 @@ const Preview: React.FC<{
|
|||||||
position: FocalPoint;
|
position: FocalPoint;
|
||||||
onPositionChange: (arg0: FocalPoint) => void;
|
onPositionChange: (arg0: FocalPoint) => void;
|
||||||
}> = ({ mediaId, position, onPositionChange }) => {
|
}> = ({ mediaId, position, onPositionChange }) => {
|
||||||
const draggingRef = useRef<boolean>(false);
|
|
||||||
const nodeRef = useRef<HTMLImageElement | HTMLVideoElement | null>(null);
|
const nodeRef = useRef<HTMLImageElement | HTMLVideoElement | null>(null);
|
||||||
|
|
||||||
|
const [dragging, setDragging] = useState<'started' | 'moving' | null>(null);
|
||||||
|
|
||||||
const [x, y] = position;
|
const [x, y] = position;
|
||||||
const style = useSpring({
|
const style = useSpring({
|
||||||
to: {
|
to: {
|
||||||
left: `${x * 100}%`,
|
left: `${x * 100}%`,
|
||||||
top: `${y * 100}%`,
|
top: `${y * 100}%`,
|
||||||
},
|
},
|
||||||
immediate: draggingRef.current,
|
immediate: dragging === 'moving',
|
||||||
});
|
});
|
||||||
const media = useAppSelector((state) =>
|
const media = useAppSelector((state) =>
|
||||||
(
|
(
|
||||||
@ -123,8 +124,6 @@ const Preview: React.FC<{
|
|||||||
me ? state.accounts.get(me) : undefined,
|
me ? state.accounts.get(me) : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [dragging, setDragging] = useState(false);
|
|
||||||
|
|
||||||
const setRef = useCallback(
|
const setRef = useCallback(
|
||||||
(e: HTMLImageElement | HTMLVideoElement | null) => {
|
(e: HTMLImageElement | HTMLVideoElement | null) => {
|
||||||
nodeRef.current = e;
|
nodeRef.current = e;
|
||||||
@ -140,20 +139,20 @@ const Preview: React.FC<{
|
|||||||
|
|
||||||
const handleMouseMove = (e: MouseEvent) => {
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
const { x, y } = getPointerPosition(nodeRef.current, e);
|
const { x, y } = getPointerPosition(nodeRef.current, e);
|
||||||
draggingRef.current = true; // This will disable the animation for quicker feedback, only do this if the mouse actually moves
|
|
||||||
|
setDragging('moving'); // This will disable the animation for quicker feedback, only do this if the mouse actually moves
|
||||||
onPositionChange([x, y]);
|
onPositionChange([x, y]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseUp = () => {
|
const handleMouseUp = () => {
|
||||||
setDragging(false);
|
setDragging(null);
|
||||||
draggingRef.current = false;
|
|
||||||
document.removeEventListener('mouseup', handleMouseUp);
|
document.removeEventListener('mouseup', handleMouseUp);
|
||||||
document.removeEventListener('mousemove', handleMouseMove);
|
document.removeEventListener('mousemove', handleMouseMove);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { x, y } = getPointerPosition(nodeRef.current, e.nativeEvent);
|
const { x, y } = getPointerPosition(nodeRef.current, e.nativeEvent);
|
||||||
|
|
||||||
setDragging(true);
|
setDragging('started');
|
||||||
onPositionChange([x, y]);
|
onPositionChange([x, y]);
|
||||||
|
|
||||||
document.addEventListener('mouseup', handleMouseUp);
|
document.addEventListener('mouseup', handleMouseUp);
|
||||||
|
|||||||
@ -31,15 +31,13 @@ export const AnnualReport: React.FC<{
|
|||||||
year: string;
|
year: string;
|
||||||
}> = ({ year }) => {
|
}> = ({ year }) => {
|
||||||
const [response, setResponse] = useState<AnnualReportResponse | null>(null);
|
const [response, setResponse] = useState<AnnualReportResponse | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(true);
|
||||||
const currentAccount = useAppSelector((state) =>
|
const currentAccount = useAppSelector((state) =>
|
||||||
me ? state.accounts.get(me) : undefined,
|
me ? state.accounts.get(me) : undefined,
|
||||||
);
|
);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
apiRequestGet<AnnualReportResponse>(`v1/annual_reports/${year}`)
|
apiRequestGet<AnnualReportResponse>(`v1/annual_reports/${year}`)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
dispatch(importFetchedStatuses(data.statuses));
|
dispatch(importFetchedStatuses(data.statuses));
|
||||||
|
|||||||
@ -55,6 +55,8 @@ const getFrequentlyUsedLanguages = createSelector(
|
|||||||
.toArray(),
|
.toArray(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isTextLongEnoughForGuess = (text: string) => text.length > 20;
|
||||||
|
|
||||||
const LanguageDropdownMenu: React.FC<{
|
const LanguageDropdownMenu: React.FC<{
|
||||||
value: string;
|
value: string;
|
||||||
guess?: string;
|
guess?: string;
|
||||||
@ -375,14 +377,27 @@ export const LanguageDropdown: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (text.length > 20) {
|
if (isTextLongEnoughForGuess(text)) {
|
||||||
debouncedGuess(text, setGuess);
|
debouncedGuess(text, setGuess);
|
||||||
} else {
|
} else {
|
||||||
debouncedGuess.cancel();
|
debouncedGuess.cancel();
|
||||||
setGuess('');
|
|
||||||
}
|
}
|
||||||
}, [text, setGuess]);
|
}, [text, setGuess]);
|
||||||
|
|
||||||
|
// Keeping track of the previous render's text length here
|
||||||
|
// to be able to reset the guess when the text length drops
|
||||||
|
// below the threshold needed to make a guess
|
||||||
|
const [wasLongText, setWasLongText] = useState(() =>
|
||||||
|
isTextLongEnoughForGuess(text),
|
||||||
|
);
|
||||||
|
if (wasLongText !== isTextLongEnoughForGuess(text)) {
|
||||||
|
setWasLongText(isTextLongEnoughForGuess(text));
|
||||||
|
|
||||||
|
if (wasLongText) {
|
||||||
|
setGuess('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={targetRef}>
|
<div ref={targetRef}>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useState, useRef, useEffect } from 'react';
|
import { useCallback, useState, useRef, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
defineMessages,
|
defineMessages,
|
||||||
@ -97,173 +97,13 @@ export const Search: React.FC<{
|
|||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const [selectedOption, setSelectedOption] = useState(-1);
|
const [selectedOption, setSelectedOption] = useState(-1);
|
||||||
const [quickActions, setQuickActions] = useState<SearchOption[]>([]);
|
const [quickActions, setQuickActions] = useState<SearchOption[]>([]);
|
||||||
useEffect(() => {
|
|
||||||
setValue(initialValue ?? '');
|
|
||||||
setQuickActions([]);
|
|
||||||
}, [initialValue]);
|
|
||||||
const searchOptions: SearchOption[] = [];
|
|
||||||
|
|
||||||
const unfocus = useCallback(() => {
|
const unfocus = useCallback(() => {
|
||||||
document.querySelector('.ui')?.parentElement?.focus();
|
document.querySelector('.ui')?.parentElement?.focus();
|
||||||
setExpanded(false);
|
setExpanded(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (searchEnabled) {
|
const insertText = useCallback((text: string) => {
|
||||||
searchOptions.push(
|
|
||||||
{
|
|
||||||
key: 'prompt-has',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>has:</mark>{' '}
|
|
||||||
<FormattedList
|
|
||||||
type='disjunction'
|
|
||||||
value={['media', 'poll', 'embed']}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('has:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-is',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>is:</mark>{' '}
|
|
||||||
<FormattedList type='disjunction' value={['reply', 'sensitive']} />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('is:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-language',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>language:</mark>{' '}
|
|
||||||
<FormattedMessage
|
|
||||||
id='search_popout.language_code'
|
|
||||||
defaultMessage='ISO language code'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('language:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-from',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>from:</mark>{' '}
|
|
||||||
<FormattedMessage id='search_popout.user' defaultMessage='user' />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('from:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-before',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>before:</mark>{' '}
|
|
||||||
<FormattedMessage
|
|
||||||
id='search_popout.specific_date'
|
|
||||||
defaultMessage='specific date'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('before:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-during',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>during:</mark>{' '}
|
|
||||||
<FormattedMessage
|
|
||||||
id='search_popout.specific_date'
|
|
||||||
defaultMessage='specific date'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('during:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-after',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>after:</mark>{' '}
|
|
||||||
<FormattedMessage
|
|
||||||
id='search_popout.specific_date'
|
|
||||||
defaultMessage='specific date'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('after:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-in',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>in:</mark>{' '}
|
|
||||||
<FormattedList
|
|
||||||
type='disjunction'
|
|
||||||
value={['all', 'library', 'public']}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('in:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const recentOptions: SearchOption[] = recent.map((search) => ({
|
|
||||||
key: `${search.type}/${search.q}`,
|
|
||||||
label: labelForRecentSearch(search),
|
|
||||||
action: () => {
|
|
||||||
setValue(search.q);
|
|
||||||
|
|
||||||
if (search.type === 'account') {
|
|
||||||
history.push(`/@${search.q}`);
|
|
||||||
} else if (search.type === 'hashtag') {
|
|
||||||
history.push(`/tags/${search.q}`);
|
|
||||||
} else {
|
|
||||||
const queryParams = new URLSearchParams({ q: search.q });
|
|
||||||
if (search.type) queryParams.set('type', search.type);
|
|
||||||
history.push({ pathname: '/search', search: queryParams.toString() });
|
|
||||||
}
|
|
||||||
|
|
||||||
unfocus();
|
|
||||||
},
|
|
||||||
forget: (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
void dispatch(forgetSearchResult(search));
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const navigableOptions = hasValue
|
|
||||||
? quickActions.concat(searchOptions)
|
|
||||||
: recentOptions.concat(quickActions, searchOptions);
|
|
||||||
|
|
||||||
const insertText = (text: string) => {
|
|
||||||
setValue((currentValue) => {
|
setValue((currentValue) => {
|
||||||
if (currentValue === '') {
|
if (currentValue === '') {
|
||||||
return text;
|
return text;
|
||||||
@ -273,7 +113,181 @@ export const Search: React.FC<{
|
|||||||
return `${currentValue} ${text}`;
|
return `${currentValue} ${text}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
|
const searchOptions = useMemo(() => {
|
||||||
|
if (!searchEnabled) {
|
||||||
|
return [];
|
||||||
|
} else {
|
||||||
|
const options: SearchOption[] = [
|
||||||
|
{
|
||||||
|
key: 'prompt-has',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>has:</mark>{' '}
|
||||||
|
<FormattedList
|
||||||
|
type='disjunction'
|
||||||
|
value={['media', 'poll', 'embed']}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('has:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-is',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>is:</mark>{' '}
|
||||||
|
<FormattedList
|
||||||
|
type='disjunction'
|
||||||
|
value={['reply', 'sensitive']}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('is:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-language',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>language:</mark>{' '}
|
||||||
|
<FormattedMessage
|
||||||
|
id='search_popout.language_code'
|
||||||
|
defaultMessage='ISO language code'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('language:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-from',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>from:</mark>{' '}
|
||||||
|
<FormattedMessage id='search_popout.user' defaultMessage='user' />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('from:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-before',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>before:</mark>{' '}
|
||||||
|
<FormattedMessage
|
||||||
|
id='search_popout.specific_date'
|
||||||
|
defaultMessage='specific date'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('before:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-during',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>during:</mark>{' '}
|
||||||
|
<FormattedMessage
|
||||||
|
id='search_popout.specific_date'
|
||||||
|
defaultMessage='specific date'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('during:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-after',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>after:</mark>{' '}
|
||||||
|
<FormattedMessage
|
||||||
|
id='search_popout.specific_date'
|
||||||
|
defaultMessage='specific date'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('after:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-in',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>in:</mark>{' '}
|
||||||
|
<FormattedList
|
||||||
|
type='disjunction'
|
||||||
|
value={['all', 'library', 'public']}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('in:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}, [insertText]);
|
||||||
|
|
||||||
|
const recentOptions: SearchOption[] = useMemo(
|
||||||
|
() =>
|
||||||
|
recent.map((search) => ({
|
||||||
|
key: `${search.type}/${search.q}`,
|
||||||
|
label: labelForRecentSearch(search),
|
||||||
|
action: () => {
|
||||||
|
setValue(search.q);
|
||||||
|
|
||||||
|
if (search.type === 'account') {
|
||||||
|
history.push(`/@${search.q}`);
|
||||||
|
} else if (search.type === 'hashtag') {
|
||||||
|
history.push(`/tags/${search.q}`);
|
||||||
|
} else {
|
||||||
|
const queryParams = new URLSearchParams({ q: search.q });
|
||||||
|
if (search.type) queryParams.set('type', search.type);
|
||||||
|
history.push({
|
||||||
|
pathname: '/search',
|
||||||
|
search: queryParams.toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
unfocus();
|
||||||
|
},
|
||||||
|
forget: (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
void dispatch(forgetSearchResult(search));
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
[dispatch, history, recent, unfocus],
|
||||||
|
);
|
||||||
|
|
||||||
|
const navigableOptions: SearchOption[] = useMemo(
|
||||||
|
() =>
|
||||||
|
hasValue
|
||||||
|
? quickActions.concat(searchOptions)
|
||||||
|
: recentOptions.concat(quickActions, searchOptions),
|
||||||
|
[hasValue, quickActions, recentOptions, searchOptions],
|
||||||
|
);
|
||||||
|
|
||||||
const submit = useCallback(
|
const submit = useCallback(
|
||||||
(q: string, type?: SearchType) => {
|
(q: string, type?: SearchType) => {
|
||||||
|
|||||||
@ -55,6 +55,11 @@ type ColumnMap = ImmutableMap<'id' | 'uuid' | 'params', string>;
|
|||||||
const glitchProbability = 1 - 0.0420215528;
|
const glitchProbability = 1 - 0.0420215528;
|
||||||
const totalElefriends = 3;
|
const totalElefriends = 3;
|
||||||
|
|
||||||
|
const pickRandomFriend = () =>
|
||||||
|
Math.random() < glitchProbability
|
||||||
|
? Math.floor(Math.random() * totalElefriends)
|
||||||
|
: totalElefriends;
|
||||||
|
|
||||||
const Compose: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
|
const Compose: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -75,11 +80,7 @@ const Compose: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
|
|||||||
false,
|
false,
|
||||||
) as boolean,
|
) as boolean,
|
||||||
);
|
);
|
||||||
const [elefriend, setElefriend] = useState(
|
const [elefriend, setElefriend] = useState(pickRandomFriend());
|
||||||
Math.random() < glitchProbability
|
|
||||||
? Math.floor(Math.random() * totalElefriends)
|
|
||||||
: totalElefriends,
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(mountCompose());
|
dispatch(mountCompose());
|
||||||
|
|||||||
@ -19,14 +19,12 @@ const messages = defineMessages({
|
|||||||
const Blocks: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
|
const Blocks: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [domains, setDomains] = useState<string[]>([]);
|
const [domains, setDomains] = useState<string[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(true);
|
||||||
const [next, setNext] = useState<string | undefined>();
|
const [next, setNext] = useState<string | undefined>();
|
||||||
const hasMore = !!next;
|
const hasMore = !!next;
|
||||||
const columnRef = useRef<ColumnRef>(null);
|
const columnRef = useRef<ColumnRef>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
void apiGetDomainBlocks()
|
void apiGetDomainBlocks()
|
||||||
.then(({ domains, links }) => {
|
.then(({ domains, links }) => {
|
||||||
const next = links.refs.find((link) => link.rel === 'next');
|
const next = links.refs.find((link) => link.rel === 'next');
|
||||||
@ -40,7 +38,7 @@ const Blocks: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
|
|||||||
.catch(() => {
|
.catch(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}, [setLoading, setDomains, setNext]);
|
}, []);
|
||||||
|
|
||||||
const handleLoadMore = useCallback(() => {
|
const handleLoadMore = useCallback(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|||||||
@ -35,12 +35,17 @@ export const Announcement: FC<AnnouncementProps> = ({
|
|||||||
}, [active, id, dispatch, read]);
|
}, [active, id, dispatch, read]);
|
||||||
|
|
||||||
// But visually show the announcement as read only when it goes out of view.
|
// But visually show the announcement as read only when it goes out of view.
|
||||||
const [unread, setUnread] = useState(!read);
|
const [isVisuallyRead, setIsVisuallyRead] = useState(read);
|
||||||
useEffect(() => {
|
const [previousActive, setPreviousActive] = useState(active);
|
||||||
if (!active && unread !== !read) {
|
if (active !== previousActive) {
|
||||||
setUnread(!read);
|
setPreviousActive(active);
|
||||||
|
|
||||||
|
// This marks the announcement as read in the UI only after it
|
||||||
|
// went from active to inactive.
|
||||||
|
if (!active && isVisuallyRead !== read) {
|
||||||
|
setIsVisuallyRead(read);
|
||||||
}
|
}
|
||||||
}, [active, unread, read]);
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateEmojiProvider>
|
<AnimateEmojiProvider>
|
||||||
@ -63,7 +68,7 @@ export const Announcement: FC<AnnouncementProps> = ({
|
|||||||
|
|
||||||
<ReactionsBar reactions={announcement.reactions} id={announcement.id} />
|
<ReactionsBar reactions={announcement.reactions} id={announcement.id} />
|
||||||
|
|
||||||
{unread && <span className='announcements__unread' />}
|
{!isVisuallyRead && <span className='announcements__unread' />}
|
||||||
</AnimateEmojiProvider>
|
</AnimateEmojiProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -164,12 +164,11 @@ const ListMembers: React.FC<{
|
|||||||
const [searching, setSearching] = useState(false);
|
const [searching, setSearching] = useState(false);
|
||||||
const [accountIds, setAccountIds] = useState<string[]>([]);
|
const [accountIds, setAccountIds] = useState<string[]>([]);
|
||||||
const [searchAccountIds, setSearchAccountIds] = useState<string[]>([]);
|
const [searchAccountIds, setSearchAccountIds] = useState<string[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(!!id);
|
||||||
const [mode, setMode] = useState<Mode>('remove');
|
const [mode, setMode] = useState<Mode>('remove');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id) {
|
if (id) {
|
||||||
setLoading(true);
|
|
||||||
dispatch(fetchList(id));
|
dispatch(fetchList(id));
|
||||||
|
|
||||||
void apiGetAccounts(id)
|
void apiGetAccounts(id)
|
||||||
|
|||||||
@ -27,17 +27,15 @@ export const ListPanel: React.FC = () => {
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const lists = useAppSelector((state) => getOrderedLists(state));
|
const lists = useAppSelector((state) => getOrderedLists(state));
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
void dispatch(fetchLists()).then(() => {
|
void dispatch(fetchLists()).then(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
}, [dispatch, setLoading]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CollapsiblePanel
|
<CollapsiblePanel
|
||||||
|
|||||||
@ -225,7 +225,7 @@ export const SearchResults: React.FC<{ multiColumn: boolean }> = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='explore__search-header'>
|
<div className='explore__search-header'>
|
||||||
<Search singleColumn initialValue={trimmedValue} />
|
<Search singleColumn initialValue={trimmedValue} key={trimmedValue} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='account__section-headline'>
|
<div className='account__section-headline'>
|
||||||
|
|||||||
@ -295,7 +295,7 @@ export const RefreshController: React.FC<{
|
|||||||
if (loadingState === 'loading') {
|
if (loadingState === 'loading') {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='load-more load-gap'
|
className='load-more load-more--large'
|
||||||
aria-busy
|
aria-busy
|
||||||
aria-live='polite'
|
aria-live='polite'
|
||||||
aria-label={intl.formatMessage(messages.loadingInitial)}
|
aria-label={intl.formatMessage(messages.loadingInitial)}
|
||||||
|
|||||||
@ -53,8 +53,6 @@ export const DomainBlockModal: React.FC<{
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
apiRequest<DomainBlockPreviewResponse>('GET', 'v1/domain_blocks/preview', {
|
apiRequest<DomainBlockPreviewResponse>('GET', 'v1/domain_blocks/preview', {
|
||||||
params: { domain },
|
params: { domain },
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
@ -68,7 +66,7 @@ export const DomainBlockModal: React.FC<{
|
|||||||
setPreview('error');
|
setPreview('error');
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}, [setPreview, setLoading, domain]);
|
}, [domain]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='modal-root__modal safety-action-modal' aria-live='polite'>
|
<div className='modal-root__modal safety-action-modal' aria-live='polite'>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
import { useEffect, useState, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { useIntl, defineMessages } from 'react-intl';
|
import { useIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
@ -41,40 +41,44 @@ const isHashtagLink = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface TargetParams {
|
interface TargetParams {
|
||||||
hashtag?: string;
|
element: HTMLAnchorElement | null;
|
||||||
accountId?: string;
|
hashtag: string;
|
||||||
|
accountId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HashtagMenuController: React.FC = () => {
|
export const HashtagMenuController: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { signedIn } = useIdentity();
|
const { signedIn } = useIdentity();
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [{ accountId, hashtag }, setTargetParams] = useState<TargetParams>({});
|
const [target, setTarget] = useState<TargetParams | null>(null);
|
||||||
const targetRef = useRef<HTMLAnchorElement | null>(null);
|
const { element = null, accountId, hashtag } = target ?? {};
|
||||||
const location = useLocation();
|
const open = !!element;
|
||||||
|
|
||||||
const account = useAppSelector((state) =>
|
const account = useAppSelector((state) =>
|
||||||
accountId ? state.accounts.get(accountId) : undefined,
|
accountId ? state.accounts.get(accountId) : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
const location = useLocation();
|
||||||
setOpen(false);
|
const [previousLocation, setPreviousLocation] = useState(location);
|
||||||
targetRef.current = null;
|
if (location !== previousLocation) {
|
||||||
}, [setOpen, location]);
|
setPreviousLocation(location);
|
||||||
|
setTarget(null);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClick = (e: MouseEvent) => {
|
const handleClick = (e: MouseEvent) => {
|
||||||
const target = (e.target as HTMLElement).closest('a');
|
const targetElement = (e.target as HTMLElement).closest('a');
|
||||||
|
|
||||||
if (e.button !== 0 || e.ctrlKey || e.metaKey) {
|
if (e.button !== 0 || e.ctrlKey || e.metaKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isHashtagLink(target)) {
|
if (!isHashtagLink(targetElement)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashtag = target.text.replace(/^#/, '');
|
const hashtag = targetElement.text.replace(/^#/, '');
|
||||||
const accountId = target.getAttribute('data-menu-hashtag');
|
const accountId = targetElement.getAttribute('data-menu-hashtag');
|
||||||
|
|
||||||
if (!hashtag || !accountId) {
|
if (!hashtag || !accountId) {
|
||||||
return;
|
return;
|
||||||
@ -82,9 +86,7 @@ export const HashtagMenuController: React.FC = () => {
|
|||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
targetRef.current = target;
|
setTarget({ element: targetElement, hashtag, accountId });
|
||||||
setOpen(true);
|
|
||||||
setTargetParams({ hashtag, accountId });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('click', handleClick, { capture: true });
|
document.addEventListener('click', handleClick, { capture: true });
|
||||||
@ -92,12 +94,11 @@ export const HashtagMenuController: React.FC = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('click', handleClick);
|
document.removeEventListener('click', handleClick);
|
||||||
};
|
};
|
||||||
}, [setTargetParams, setOpen]);
|
}, []);
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setOpen(false);
|
setTarget(null);
|
||||||
targetRef.current = null;
|
}, []);
|
||||||
}, [setOpen]);
|
|
||||||
|
|
||||||
const menu = useMemo(() => {
|
const menu = useMemo(() => {
|
||||||
const arr: MenuItem[] = [
|
const arr: MenuItem[] = [
|
||||||
@ -139,7 +140,7 @@ export const HashtagMenuController: React.FC = () => {
|
|||||||
offset={offset}
|
offset={offset}
|
||||||
placement='bottom'
|
placement='bottom'
|
||||||
flip
|
flip
|
||||||
target={targetRef}
|
target={element}
|
||||||
popperConfig={popperConfig}
|
popperConfig={popperConfig}
|
||||||
>
|
>
|
||||||
{({ props, arrowProps, placement }) => (
|
{({ props, arrowProps, placement }) => (
|
||||||
|
|||||||
@ -66,6 +66,7 @@ export const MediaModal: FC<MediaModalProps> = forwardRef<
|
|||||||
_ref,
|
_ref,
|
||||||
) => {
|
) => {
|
||||||
const [index, setIndex] = useState(startIndex);
|
const [index, setIndex] = useState(startIndex);
|
||||||
|
const [zoomedIn, setZoomedIn] = useState(false);
|
||||||
const currentMedia = media.get(index);
|
const currentMedia = media.get(index);
|
||||||
|
|
||||||
const handleChangeIndex = useCallback(
|
const handleChangeIndex = useCallback(
|
||||||
@ -134,7 +135,6 @@ export const MediaModal: FC<MediaModalProps> = forwardRef<
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [zoomedIn, setZoomedIn] = useState(false);
|
|
||||||
const zoomable =
|
const zoomable =
|
||||||
currentMedia?.get('type') === 'image' &&
|
currentMedia?.get('type') === 'image' &&
|
||||||
((currentMedia.getIn(['meta', 'original', 'width']) as number) >
|
((currentMedia.getIn(['meta', 'original', 'width']) as number) >
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useSyncExternalStore } from 'react';
|
||||||
|
|
||||||
const breakpoints = {
|
const breakpoints = {
|
||||||
narrow: 479, // Device width under which horizontal space is constrained
|
narrow: 479, // Device width under which horizontal space is constrained
|
||||||
@ -9,25 +9,20 @@ const breakpoints = {
|
|||||||
type Breakpoint = keyof typeof breakpoints;
|
type Breakpoint = keyof typeof breakpoints;
|
||||||
|
|
||||||
export const useBreakpoint = (breakpoint: Breakpoint) => {
|
export const useBreakpoint = (breakpoint: Breakpoint) => {
|
||||||
const [isMatching, setIsMatching] = useState(false);
|
const query = `(max-width: ${breakpoints[breakpoint]}px)`;
|
||||||
|
|
||||||
useEffect(() => {
|
const isMatching = useSyncExternalStore(
|
||||||
const mediaWatcher = window.matchMedia(
|
(callback) => {
|
||||||
`(max-width: ${breakpoints[breakpoint]}px)`,
|
const mediaWatcher = window.matchMedia(query);
|
||||||
);
|
|
||||||
|
|
||||||
setIsMatching(mediaWatcher.matches);
|
mediaWatcher.addEventListener('change', callback);
|
||||||
|
|
||||||
const handleChange = (e: MediaQueryListEvent) => {
|
return () => {
|
||||||
setIsMatching(e.matches);
|
mediaWatcher.removeEventListener('change', callback);
|
||||||
};
|
};
|
||||||
|
},
|
||||||
mediaWatcher.addEventListener('change', handleChange);
|
() => window.matchMedia(query).matches,
|
||||||
|
);
|
||||||
return () => {
|
|
||||||
mediaWatcher.removeEventListener('change', handleChange);
|
|
||||||
};
|
|
||||||
}, [breakpoint, setIsMatching]);
|
|
||||||
|
|
||||||
return isMatching;
|
return isMatching;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useRef, useEffect } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the previous state of the passed in value.
|
* Returns the previous state of the passed in value.
|
||||||
@ -6,11 +6,21 @@ import { useRef, useEffect } from 'react';
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export function usePrevious<T>(value: T): T | undefined {
|
export function usePrevious<T>(value: T): T | undefined {
|
||||||
const ref = useRef<T>();
|
const [{ previous, current }, setMemory] = useState<{
|
||||||
|
previous: T | undefined;
|
||||||
|
current: T;
|
||||||
|
}>(() => ({ previous: undefined, current: value }));
|
||||||
|
|
||||||
useEffect(() => {
|
let result = previous;
|
||||||
ref.current = value;
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
return ref.current;
|
if (value !== current) {
|
||||||
|
setMemory({
|
||||||
|
previous: current,
|
||||||
|
current: value,
|
||||||
|
});
|
||||||
|
// Ensure that the returned result updates synchronously
|
||||||
|
result = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3246,20 +3246,21 @@ a.account__display-name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.column__alert {
|
.column__alert {
|
||||||
|
--alert-height: 54px;
|
||||||
|
|
||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
grid-template-rows: minmax(var(--alert-height), max-content);
|
||||||
|
align-items: end;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 360px;
|
max-width: 360px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin: auto auto 0;
|
margin: auto auto 0;
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
|
pointer-events: none;
|
||||||
&:empty {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: #{$mobile-menu-breakpoint - 1}) {
|
@media (max-width: #{$mobile-menu-breakpoint - 1}) {
|
||||||
// Compensate for mobile menubar
|
// Compensate for mobile menubar
|
||||||
@ -3270,6 +3271,7 @@ a.account__display-name {
|
|||||||
// Make all nested alerts occupy the same space
|
// Make all nested alerts occupy the same space
|
||||||
// rather than stack
|
// rather than stack
|
||||||
grid-area: 1 / 1;
|
grid-area: 1 / 1;
|
||||||
|
pointer-events: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4572,13 +4574,19 @@ a.status-card {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
&:hover {
|
&--large {
|
||||||
background: var(--on-surface-color);
|
padding-block: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:is(button) {
|
||||||
outline: 2px solid $ui-button-focus-outline-color;
|
&:hover {
|
||||||
outline-offset: -2px;
|
background: var(--on-surface-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 2px solid $ui-button-focus-outline-color;
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@ -6433,6 +6441,8 @@ a.status-card {
|
|||||||
inset-inline-start: 0;
|
inset-inline-start: 0;
|
||||||
inset-inline-end: 0;
|
inset-inline-end: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around; // If set to center, the fullscreen image overlay is misaligned.
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export const AltTextBadge: React.FC<{ description: string }> = ({
|
|||||||
rootClose
|
rootClose
|
||||||
onHide={handleClose}
|
onHide={handleClose}
|
||||||
show={open}
|
show={open}
|
||||||
target={anchorRef.current}
|
target={anchorRef}
|
||||||
placement='top-end'
|
placement='top-end'
|
||||||
flip
|
flip
|
||||||
offset={offset}
|
offset={offset}
|
||||||
|
|||||||
@ -76,6 +76,11 @@ export const Carousel = <
|
|||||||
// Handle slide change
|
// Handle slide change
|
||||||
const [slideIndex, setSlideIndex] = useState(0);
|
const [slideIndex, setSlideIndex] = useState(0);
|
||||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
|
// Handle slide heights
|
||||||
|
const [currentSlideHeight, setCurrentSlideHeight] = useState(
|
||||||
|
() => wrapperRef.current?.scrollHeight ?? 0,
|
||||||
|
);
|
||||||
|
const previousSlideHeight = usePrevious(currentSlideHeight);
|
||||||
const handleSlideChange = useCallback(
|
const handleSlideChange = useCallback(
|
||||||
(direction: number) => {
|
(direction: number) => {
|
||||||
setSlideIndex((prev) => {
|
setSlideIndex((prev) => {
|
||||||
@ -101,16 +106,11 @@ export const Carousel = <
|
|||||||
[items.length, onChangeSlide],
|
[items.length, onChangeSlide],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle slide heights
|
const observerRef = useRef<ResizeObserver | null>(null);
|
||||||
const [currentSlideHeight, setCurrentSlideHeight] = useState(
|
observerRef.current ??= new ResizeObserver(() => {
|
||||||
wrapperRef.current?.scrollHeight ?? 0,
|
handleSlideChange(0);
|
||||||
);
|
});
|
||||||
const previousSlideHeight = usePrevious(currentSlideHeight);
|
|
||||||
const observerRef = useRef<ResizeObserver>(
|
|
||||||
new ResizeObserver(() => {
|
|
||||||
handleSlideChange(0);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const wrapperStyles = useSpring({
|
const wrapperStyles = useSpring({
|
||||||
x: `-${slideIndex * 100}%`,
|
x: `-${slideIndex * 100}%`,
|
||||||
height: currentSlideHeight,
|
height: currentSlideHeight,
|
||||||
@ -200,7 +200,7 @@ export const Carousel = <
|
|||||||
};
|
};
|
||||||
|
|
||||||
type CarouselSlideWrapperProps<SlideProps extends CarouselSlideProps> = {
|
type CarouselSlideWrapperProps<SlideProps extends CarouselSlideProps> = {
|
||||||
observer: ResizeObserver;
|
observer: ResizeObserver | null;
|
||||||
className: string;
|
className: string;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
item: SlideProps;
|
item: SlideProps;
|
||||||
@ -217,7 +217,7 @@ const CarouselSlideWrapper = <SlideProps extends CarouselSlideProps>({
|
|||||||
}: CarouselSlideWrapperProps<SlideProps>) => {
|
}: CarouselSlideWrapperProps<SlideProps>) => {
|
||||||
const handleRef = useCallback(
|
const handleRef = useCallback(
|
||||||
(instance: HTMLDivElement | null) => {
|
(instance: HTMLDivElement | null) => {
|
||||||
if (instance) {
|
if (observer && instance) {
|
||||||
observer.observe(instance);
|
observer.observe(instance);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useState, useEffect, useRef } from 'react';
|
import { useCallback, useState, useRef } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
@ -12,11 +12,15 @@ export const ColumnSearchHeader: React.FC<{
|
|||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
// Reset the component when it turns from active to inactive.
|
||||||
|
// [More on this pattern](https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes)
|
||||||
|
const [previousActive, setPreviousActive] = useState(active);
|
||||||
|
if (active !== previousActive) {
|
||||||
|
setPreviousActive(active);
|
||||||
if (!active) {
|
if (!active) {
|
||||||
setValue('');
|
setValue('');
|
||||||
}
|
}
|
||||||
}, [active]);
|
}
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
|
({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
|||||||
@ -109,7 +109,7 @@ export const Dropdown: FC<
|
|||||||
placement='bottom-start'
|
placement='bottom-start'
|
||||||
onHide={handleClose}
|
onHide={handleClose}
|
||||||
flip
|
flip
|
||||||
target={buttonRef.current}
|
target={buttonRef}
|
||||||
popperConfig={{
|
popperConfig={{
|
||||||
strategy: 'fixed',
|
strategy: 'fixed',
|
||||||
modifiers: [matchWidth],
|
modifiers: [matchWidth],
|
||||||
|
|||||||
@ -42,16 +42,10 @@ import { IconButton } from './icon_button';
|
|||||||
|
|
||||||
let id = 0;
|
let id = 0;
|
||||||
|
|
||||||
export interface RenderItemFnHandlers {
|
|
||||||
onClick: React.MouseEventHandler;
|
|
||||||
onKeyUp: React.KeyboardEventHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RenderItemFn<Item = MenuItem> = (
|
export type RenderItemFn<Item = MenuItem> = (
|
||||||
item: Item,
|
item: Item,
|
||||||
index: number,
|
index: number,
|
||||||
handlers: RenderItemFnHandlers,
|
onClick: React.MouseEventHandler,
|
||||||
focusRefCallback?: (c: HTMLAnchorElement | HTMLButtonElement | null) => void,
|
|
||||||
) => React.ReactNode;
|
) => React.ReactNode;
|
||||||
|
|
||||||
type ItemClickFn<Item = MenuItem> = (item: Item, index: number) => void;
|
type ItemClickFn<Item = MenuItem> = (item: Item, index: number) => void;
|
||||||
@ -101,7 +95,6 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
onItemClick,
|
onItemClick,
|
||||||
}: DropdownMenuProps<Item>) => {
|
}: DropdownMenuProps<Item>) => {
|
||||||
const nodeRef = useRef<HTMLDivElement>(null);
|
const nodeRef = useRef<HTMLDivElement>(null);
|
||||||
const focusedItemRef = useRef<HTMLElement | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleDocumentClick = (e: MouseEvent) => {
|
const handleDocumentClick = (e: MouseEvent) => {
|
||||||
@ -163,8 +156,11 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
document.addEventListener('click', handleDocumentClick, { capture: true });
|
document.addEventListener('click', handleDocumentClick, { capture: true });
|
||||||
document.addEventListener('keydown', handleKeyDown, { capture: true });
|
document.addEventListener('keydown', handleKeyDown, { capture: true });
|
||||||
|
|
||||||
if (focusedItemRef.current && openedViaKeyboard) {
|
if (openedViaKeyboard) {
|
||||||
focusedItemRef.current.focus({ preventScroll: true });
|
const firstMenuItem = nodeRef.current?.querySelector<
|
||||||
|
HTMLAnchorElement | HTMLButtonElement
|
||||||
|
>('li:first-child > :is(a, button)');
|
||||||
|
firstMenuItem?.focus({ preventScroll: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -175,13 +171,6 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
};
|
};
|
||||||
}, [onClose, openedViaKeyboard]);
|
}, [onClose, openedViaKeyboard]);
|
||||||
|
|
||||||
const handleFocusedItemRef = useCallback(
|
|
||||||
(c: HTMLAnchorElement | HTMLButtonElement | null) => {
|
|
||||||
focusedItemRef.current = c as HTMLElement;
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleItemClick = useCallback(
|
const handleItemClick = useCallback(
|
||||||
(e: React.MouseEvent | React.KeyboardEvent) => {
|
(e: React.MouseEvent | React.KeyboardEvent) => {
|
||||||
const i = Number(e.currentTarget.getAttribute('data-index'));
|
const i = Number(e.currentTarget.getAttribute('data-index'));
|
||||||
@ -207,15 +196,6 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
[onClose, onItemClick, items],
|
[onClose, onItemClick, items],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleItemKeyUp = useCallback(
|
|
||||||
(e: React.KeyboardEvent) => {
|
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
|
||||||
handleItemClick(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[handleItemClick],
|
|
||||||
);
|
|
||||||
|
|
||||||
const nativeRenderItem = (option: Item, i: number) => {
|
const nativeRenderItem = (option: Item, i: number) => {
|
||||||
if (!isMenuItem(option)) {
|
if (!isMenuItem(option)) {
|
||||||
return null;
|
return null;
|
||||||
@ -232,9 +212,7 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
if (isActionItem(option)) {
|
if (isActionItem(option)) {
|
||||||
element = (
|
element = (
|
||||||
<button
|
<button
|
||||||
ref={i === 0 ? handleFocusedItemRef : undefined}
|
|
||||||
onClick={handleItemClick}
|
onClick={handleItemClick}
|
||||||
onKeyUp={handleItemKeyUp}
|
|
||||||
data-index={i}
|
data-index={i}
|
||||||
aria-disabled={disabled}
|
aria-disabled={disabled}
|
||||||
type='button'
|
type='button'
|
||||||
@ -249,9 +227,7 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
target={option.target ?? '_target'}
|
target={option.target ?? '_target'}
|
||||||
data-method={option.method}
|
data-method={option.method}
|
||||||
rel='noopener'
|
rel='noopener'
|
||||||
ref={i === 0 ? handleFocusedItemRef : undefined}
|
|
||||||
onClick={handleItemClick}
|
onClick={handleItemClick}
|
||||||
onKeyUp={handleItemKeyUp}
|
|
||||||
data-index={i}
|
data-index={i}
|
||||||
>
|
>
|
||||||
<DropdownMenuItemContent item={option} />
|
<DropdownMenuItemContent item={option} />
|
||||||
@ -259,13 +235,7 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
element = (
|
element = (
|
||||||
<Link
|
<Link to={option.to} onClick={handleItemClick} data-index={i}>
|
||||||
to={option.to}
|
|
||||||
ref={i === 0 ? handleFocusedItemRef : undefined}
|
|
||||||
onClick={handleItemClick}
|
|
||||||
onKeyUp={handleItemKeyUp}
|
|
||||||
data-index={i}
|
|
||||||
>
|
|
||||||
<DropdownMenuItemContent item={option} />
|
<DropdownMenuItemContent item={option} />
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@ -308,15 +278,7 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{items.map((option, i) =>
|
{items.map((option, i) =>
|
||||||
renderItemMethod(
|
renderItemMethod(option, i, handleItemClick),
|
||||||
option,
|
|
||||||
i,
|
|
||||||
{
|
|
||||||
onClick: handleItemClick,
|
|
||||||
onKeyUp: handleItemKeyUp,
|
|
||||||
},
|
|
||||||
i === 0 ? handleFocusedItemRef : undefined,
|
|
||||||
),
|
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
@ -400,7 +362,7 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
}, [dispatch, currentId]);
|
}, [dispatch, currentId]);
|
||||||
|
|
||||||
const handleItemClick = useCallback(
|
const handleItemClick = useCallback(
|
||||||
(e: React.MouseEvent | React.KeyboardEvent) => {
|
(e: React.MouseEvent) => {
|
||||||
const i = Number(e.currentTarget.getAttribute('data-index'));
|
const i = Number(e.currentTarget.getAttribute('data-index'));
|
||||||
const item = items?.[i];
|
const item = items?.[i];
|
||||||
|
|
||||||
@ -421,10 +383,20 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
[handleClose, onItemClick, items],
|
[handleClose, onItemClick, items],
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleDropdown = useCallback(
|
const isKeypressRef = useRef(false);
|
||||||
(e: React.MouseEvent | React.KeyboardEvent) => {
|
|
||||||
const { type } = e;
|
|
||||||
|
|
||||||
|
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
||||||
|
if (e.key === ' ' || e.key === 'Enter') {
|
||||||
|
isKeypressRef.current = true;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const unsetIsKeypress = useCallback(() => {
|
||||||
|
isKeypressRef.current = false;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggleDropdown = useCallback(
|
||||||
|
(e: React.MouseEvent) => {
|
||||||
if (open) {
|
if (open) {
|
||||||
handleClose();
|
handleClose();
|
||||||
} else {
|
} else {
|
||||||
@ -451,10 +423,11 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
dispatch(
|
dispatch(
|
||||||
openDropdownMenu({
|
openDropdownMenu({
|
||||||
id: currentId,
|
id: currentId,
|
||||||
keyboard: type !== 'click',
|
keyboard: isKeypressRef.current,
|
||||||
scrollKey,
|
scrollKey,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
isKeypressRef.current = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -485,6 +458,9 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||||||
const buttonProps = {
|
const buttonProps = {
|
||||||
disabled,
|
disabled,
|
||||||
onClick: toggleDropdown,
|
onClick: toggleDropdown,
|
||||||
|
onKeyDown: handleKeyDown,
|
||||||
|
onKeyUp: unsetIsKeypress,
|
||||||
|
onBlur: unsetIsKeypress,
|
||||||
'aria-expanded': open,
|
'aria-expanded': open,
|
||||||
'aria-controls': menuId,
|
'aria-controls': menuId,
|
||||||
ref: buttonRef,
|
ref: buttonRef,
|
||||||
|
|||||||
@ -58,17 +58,7 @@ export const EditedTimestamp: React.FC<{
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
(
|
(item: HistoryItem, index: number, onClick: React.MouseEventHandler) => {
|
||||||
item: HistoryItem,
|
|
||||||
index: number,
|
|
||||||
{
|
|
||||||
onClick,
|
|
||||||
onKeyUp,
|
|
||||||
}: {
|
|
||||||
onClick: React.MouseEventHandler;
|
|
||||||
onKeyUp: React.KeyboardEventHandler;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
const formattedDate = (
|
const formattedDate = (
|
||||||
<RelativeTimestamp
|
<RelativeTimestamp
|
||||||
timestamp={item.get('created_at') as string}
|
timestamp={item.get('created_at') as string}
|
||||||
@ -98,12 +88,7 @@ export const EditedTimestamp: React.FC<{
|
|||||||
className='dropdown-menu__item edited-timestamp__history__item'
|
className='dropdown-menu__item edited-timestamp__history__item'
|
||||||
key={item.get('created_at') as string}
|
key={item.get('created_at') as string}
|
||||||
>
|
>
|
||||||
<button
|
<button data-index={index} onClick={onClick} type='button'>
|
||||||
data-index={index}
|
|
||||||
onClick={onClick}
|
|
||||||
onKeyUp={onKeyUp}
|
|
||||||
type='button'
|
|
||||||
>
|
|
||||||
{label}
|
{label}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@ -27,22 +27,23 @@ export const ExitAnimationWrapper: React.FC<{
|
|||||||
*/
|
*/
|
||||||
children: (delayedIsActive: boolean) => React.ReactNode;
|
children: (delayedIsActive: boolean) => React.ReactNode;
|
||||||
}> = ({ isActive = false, delayMs = 500, withEntryDelay, children }) => {
|
}> = ({ isActive = false, delayMs = 500, withEntryDelay, children }) => {
|
||||||
const [delayedIsActive, setDelayedIsActive] = useState(false);
|
const [delayedIsActive, setDelayedIsActive] = useState(
|
||||||
|
isActive && !withEntryDelay,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isActive && !withEntryDelay) {
|
const withDelay = !isActive || withEntryDelay;
|
||||||
setDelayedIsActive(true);
|
|
||||||
|
|
||||||
return () => '';
|
const timeout = setTimeout(
|
||||||
} else {
|
() => {
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
setDelayedIsActive(isActive);
|
setDelayedIsActive(isActive);
|
||||||
}, delayMs);
|
},
|
||||||
|
withDelay ? delayMs : 0,
|
||||||
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}, [isActive, delayMs, withEntryDelay]);
|
}, [isActive, delayMs, withEntryDelay]);
|
||||||
|
|
||||||
if (!isActive && !delayedIsActive) {
|
if (!isActive && !delayedIsActive) {
|
||||||
|
|||||||
@ -27,7 +27,6 @@ export const HoverCardController: React.FC = () => {
|
|||||||
const [setLeaveTimeout, cancelLeaveTimeout] = useTimeout();
|
const [setLeaveTimeout, cancelLeaveTimeout] = useTimeout();
|
||||||
const [setEnterTimeout, cancelEnterTimeout, delayEnterTimeout] = useTimeout();
|
const [setEnterTimeout, cancelEnterTimeout, delayEnterTimeout] = useTimeout();
|
||||||
const [setScrollTimeout] = useTimeout();
|
const [setScrollTimeout] = useTimeout();
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
cancelEnterTimeout();
|
cancelEnterTimeout();
|
||||||
@ -36,9 +35,12 @@ export const HoverCardController: React.FC = () => {
|
|||||||
setAnchor(null);
|
setAnchor(null);
|
||||||
}, [cancelEnterTimeout, cancelLeaveTimeout, setOpen, setAnchor]);
|
}, [cancelEnterTimeout, cancelLeaveTimeout, setOpen, setAnchor]);
|
||||||
|
|
||||||
useEffect(() => {
|
const location = useLocation();
|
||||||
|
const [previousLocation, setPreviousLocation] = useState(location);
|
||||||
|
if (location !== previousLocation) {
|
||||||
|
setPreviousLocation(location);
|
||||||
handleClose();
|
handleClose();
|
||||||
}, [handleClose, location]);
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isScrolling = false;
|
let isScrolling = false;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useCallback, forwardRef } from 'react';
|
import { useCallback, forwardRef } from 'react';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
@ -55,23 +55,6 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(
|
|||||||
},
|
},
|
||||||
buttonRef,
|
buttonRef,
|
||||||
) => {
|
) => {
|
||||||
const [activate, setActivate] = useState(false);
|
|
||||||
const [deactivate, setDeactivate] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!animate) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activate && !active) {
|
|
||||||
setActivate(false);
|
|
||||||
setDeactivate(true);
|
|
||||||
} else if (!activate && active) {
|
|
||||||
setActivate(true);
|
|
||||||
setDeactivate(false);
|
|
||||||
}
|
|
||||||
}, [setActivate, setDeactivate, animate, active, activate]);
|
|
||||||
|
|
||||||
const handleClick: React.MouseEventHandler<HTMLButtonElement> = useCallback(
|
const handleClick: React.MouseEventHandler<HTMLButtonElement> = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -112,8 +95,8 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(
|
|||||||
active,
|
active,
|
||||||
disabled,
|
disabled,
|
||||||
inverted,
|
inverted,
|
||||||
activate,
|
activate: animate && active,
|
||||||
deactivate,
|
deactivate: animate && !active,
|
||||||
overlayed: overlay,
|
overlayed: overlay,
|
||||||
'icon-button--with-counter': typeof counter !== 'undefined',
|
'icon-button--with-counter': typeof counter !== 'undefined',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -35,6 +35,9 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isPollExpired = (expiresAt: Model.Poll['expires_at']) =>
|
||||||
|
new Date(expiresAt).getTime() < Date.now();
|
||||||
|
|
||||||
interface PollProps {
|
interface PollProps {
|
||||||
pollId: string;
|
pollId: string;
|
||||||
status: Status;
|
status: Status;
|
||||||
@ -58,8 +61,7 @@ export const Poll: React.FC<PollProps> = ({ pollId, disabled, status }) => {
|
|||||||
if (!poll) {
|
if (!poll) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const expiresAt = poll.expires_at;
|
return poll.expired || isPollExpired(poll.expires_at);
|
||||||
return poll.expired || new Date(expiresAt).getTime() < Date.now();
|
|
||||||
}, [poll]);
|
}, [poll]);
|
||||||
const timeRemaining = useMemo(() => {
|
const timeRemaining = useMemo(() => {
|
||||||
if (!poll) {
|
if (!poll) {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import type { Status } from '@/mastodon/models/status';
|
|||||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||||
import type { SomeRequired } from '@/mastodon/utils/types';
|
import type { SomeRequired } from '@/mastodon/utils/types';
|
||||||
|
|
||||||
import type { RenderItemFn, RenderItemFnHandlers } from '../dropdown_menu';
|
import type { RenderItemFn } from '../dropdown_menu';
|
||||||
import { Dropdown, DropdownMenuItemContent } from '../dropdown_menu';
|
import { Dropdown, DropdownMenuItemContent } from '../dropdown_menu';
|
||||||
import { IconButton } from '../icon_button';
|
import { IconButton } from '../icon_button';
|
||||||
|
|
||||||
@ -74,18 +74,12 @@ const StandaloneBoostButton: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderMenuItem: RenderItemFn<ActionMenuItem> = (
|
const renderMenuItem: RenderItemFn<ActionMenuItem> = (item, index, onClick) => (
|
||||||
item,
|
|
||||||
index,
|
|
||||||
handlers,
|
|
||||||
focusRefCallback,
|
|
||||||
) => (
|
|
||||||
<ReblogMenuItem
|
<ReblogMenuItem
|
||||||
index={index}
|
index={index}
|
||||||
item={item}
|
item={item}
|
||||||
handlers={handlers}
|
onClick={onClick}
|
||||||
key={`${item.text}-${index}`}
|
key={`${item.text}-${index}`}
|
||||||
focusRefCallback={focusRefCallback}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -118,6 +112,18 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
const statusId = status.get('id') as string;
|
const statusId = status.get('id') as string;
|
||||||
const wasBoosted = !!status.get('reblogged');
|
const wasBoosted = !!status.get('reblogged');
|
||||||
|
|
||||||
|
let count: number | undefined;
|
||||||
|
if (counters) {
|
||||||
|
count = 0;
|
||||||
|
// Ensure count is a valid integer.
|
||||||
|
if (Number.isInteger(status.get('reblogs_count'))) {
|
||||||
|
count += status.get('reblogs_count') as number;
|
||||||
|
}
|
||||||
|
if (Number.isInteger(status.get('quotes_count'))) {
|
||||||
|
count += status.get('quotes_count') as number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const showLoginPrompt = useCallback(() => {
|
const showLoginPrompt = useCallback(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
openModal({
|
openModal({
|
||||||
@ -193,12 +199,7 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
)}
|
)}
|
||||||
icon='retweet'
|
icon='retweet'
|
||||||
iconComponent={boostIcon}
|
iconComponent={boostIcon}
|
||||||
counter={
|
counter={count}
|
||||||
counters
|
|
||||||
? (status.get('reblogs_count') as number) +
|
|
||||||
(status.get('quotes_count') as number)
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
active={isReblogged}
|
active={isReblogged}
|
||||||
/>
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
@ -208,16 +209,10 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
interface ReblogMenuItemProps {
|
interface ReblogMenuItemProps {
|
||||||
item: ActionMenuItem;
|
item: ActionMenuItem;
|
||||||
index: number;
|
index: number;
|
||||||
handlers: RenderItemFnHandlers;
|
onClick: React.MouseEventHandler;
|
||||||
focusRefCallback?: (c: HTMLAnchorElement | HTMLButtonElement | null) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReblogMenuItem: FC<ReblogMenuItemProps> = ({
|
const ReblogMenuItem: FC<ReblogMenuItemProps> = ({ index, item, onClick }) => {
|
||||||
index,
|
|
||||||
item,
|
|
||||||
handlers,
|
|
||||||
focusRefCallback,
|
|
||||||
}) => {
|
|
||||||
const { text, highlighted, disabled } = item;
|
const { text, highlighted, disabled } = item;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -228,8 +223,7 @@ const ReblogMenuItem: FC<ReblogMenuItemProps> = ({
|
|||||||
key={`${text}-${index}`}
|
key={`${text}-${index}`}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
{...handlers}
|
onClick={onClick}
|
||||||
ref={focusRefCallback}
|
|
||||||
aria-disabled={disabled}
|
aria-disabled={disabled}
|
||||||
data-index={index}
|
data-index={index}
|
||||||
type='button'
|
type='button'
|
||||||
|
|||||||
@ -44,6 +44,7 @@ export const RemoveQuoteHint: React.FC<{
|
|||||||
|
|
||||||
if (!firstHintId) {
|
if (!firstHintId) {
|
||||||
firstHintId = uniqueId;
|
firstHintId = uniqueId;
|
||||||
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
setIsOnlyHint(true);
|
setIsOnlyHint(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,8 +65,8 @@ export const RemoveQuoteHint: React.FC<{
|
|||||||
flip
|
flip
|
||||||
offset={[12, 10]}
|
offset={[12, 10]}
|
||||||
placement='bottom-end'
|
placement='bottom-end'
|
||||||
target={anchorRef.current}
|
target={anchorRef}
|
||||||
container={anchorRef.current}
|
container={anchorRef}
|
||||||
>
|
>
|
||||||
{({ props, placement }) => (
|
{({ props, placement }) => (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -86,6 +86,7 @@ export const ScrollContext: React.FC<ScrollContextProps> = ({
|
|||||||
) =>
|
) =>
|
||||||
// Hack to allow accessing scrollBehavior._stateStorage
|
// Hack to allow accessing scrollBehavior._stateStorage
|
||||||
shouldUpdateScroll.call(
|
shouldUpdateScroll.call(
|
||||||
|
// eslint-disable-next-line react-hooks/immutability
|
||||||
scrollBehavior,
|
scrollBehavior,
|
||||||
prevLocationContext,
|
prevLocationContext,
|
||||||
locationContext,
|
locationContext,
|
||||||
|
|||||||
@ -101,16 +101,17 @@ const Preview: React.FC<{
|
|||||||
position: FocalPoint;
|
position: FocalPoint;
|
||||||
onPositionChange: (arg0: FocalPoint) => void;
|
onPositionChange: (arg0: FocalPoint) => void;
|
||||||
}> = ({ mediaId, position, onPositionChange }) => {
|
}> = ({ mediaId, position, onPositionChange }) => {
|
||||||
const draggingRef = useRef<boolean>(false);
|
|
||||||
const nodeRef = useRef<HTMLImageElement | HTMLVideoElement | null>(null);
|
const nodeRef = useRef<HTMLImageElement | HTMLVideoElement | null>(null);
|
||||||
|
|
||||||
|
const [dragging, setDragging] = useState<'started' | 'moving' | null>(null);
|
||||||
|
|
||||||
const [x, y] = position;
|
const [x, y] = position;
|
||||||
const style = useSpring({
|
const style = useSpring({
|
||||||
to: {
|
to: {
|
||||||
left: `${x * 100}%`,
|
left: `${x * 100}%`,
|
||||||
top: `${y * 100}%`,
|
top: `${y * 100}%`,
|
||||||
},
|
},
|
||||||
immediate: draggingRef.current,
|
immediate: dragging === 'moving',
|
||||||
});
|
});
|
||||||
const media = useAppSelector((state) =>
|
const media = useAppSelector((state) =>
|
||||||
(
|
(
|
||||||
@ -123,8 +124,6 @@ const Preview: React.FC<{
|
|||||||
me ? state.accounts.get(me) : undefined,
|
me ? state.accounts.get(me) : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [dragging, setDragging] = useState(false);
|
|
||||||
|
|
||||||
const setRef = useCallback(
|
const setRef = useCallback(
|
||||||
(e: HTMLImageElement | HTMLVideoElement | null) => {
|
(e: HTMLImageElement | HTMLVideoElement | null) => {
|
||||||
nodeRef.current = e;
|
nodeRef.current = e;
|
||||||
@ -140,20 +139,20 @@ const Preview: React.FC<{
|
|||||||
|
|
||||||
const handleMouseMove = (e: MouseEvent) => {
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
const { x, y } = getPointerPosition(nodeRef.current, e);
|
const { x, y } = getPointerPosition(nodeRef.current, e);
|
||||||
draggingRef.current = true; // This will disable the animation for quicker feedback, only do this if the mouse actually moves
|
|
||||||
|
setDragging('moving'); // This will disable the animation for quicker feedback, only do this if the mouse actually moves
|
||||||
onPositionChange([x, y]);
|
onPositionChange([x, y]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseUp = () => {
|
const handleMouseUp = () => {
|
||||||
setDragging(false);
|
setDragging(null);
|
||||||
draggingRef.current = false;
|
|
||||||
document.removeEventListener('mouseup', handleMouseUp);
|
document.removeEventListener('mouseup', handleMouseUp);
|
||||||
document.removeEventListener('mousemove', handleMouseMove);
|
document.removeEventListener('mousemove', handleMouseMove);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { x, y } = getPointerPosition(nodeRef.current, e.nativeEvent);
|
const { x, y } = getPointerPosition(nodeRef.current, e.nativeEvent);
|
||||||
|
|
||||||
setDragging(true);
|
setDragging('started');
|
||||||
onPositionChange([x, y]);
|
onPositionChange([x, y]);
|
||||||
|
|
||||||
document.addEventListener('mouseup', handleMouseUp);
|
document.addEventListener('mouseup', handleMouseUp);
|
||||||
|
|||||||
@ -31,15 +31,13 @@ export const AnnualReport: React.FC<{
|
|||||||
year: string;
|
year: string;
|
||||||
}> = ({ year }) => {
|
}> = ({ year }) => {
|
||||||
const [response, setResponse] = useState<AnnualReportResponse | null>(null);
|
const [response, setResponse] = useState<AnnualReportResponse | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(true);
|
||||||
const currentAccount = useAppSelector((state) =>
|
const currentAccount = useAppSelector((state) =>
|
||||||
me ? state.accounts.get(me) : undefined,
|
me ? state.accounts.get(me) : undefined,
|
||||||
);
|
);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
apiRequestGet<AnnualReportResponse>(`v1/annual_reports/${year}`)
|
apiRequestGet<AnnualReportResponse>(`v1/annual_reports/${year}`)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
dispatch(importFetchedStatuses(data.statuses));
|
dispatch(importFetchedStatuses(data.statuses));
|
||||||
|
|||||||
@ -55,6 +55,8 @@ const getFrequentlyUsedLanguages = createSelector(
|
|||||||
.toArray(),
|
.toArray(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isTextLongEnoughForGuess = (text: string) => text.length > 20;
|
||||||
|
|
||||||
const LanguageDropdownMenu: React.FC<{
|
const LanguageDropdownMenu: React.FC<{
|
||||||
value: string;
|
value: string;
|
||||||
guess?: string;
|
guess?: string;
|
||||||
@ -375,14 +377,27 @@ export const LanguageDropdown: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (text.length > 20) {
|
if (isTextLongEnoughForGuess(text)) {
|
||||||
debouncedGuess(text, setGuess);
|
debouncedGuess(text, setGuess);
|
||||||
} else {
|
} else {
|
||||||
debouncedGuess.cancel();
|
debouncedGuess.cancel();
|
||||||
setGuess('');
|
|
||||||
}
|
}
|
||||||
}, [text, setGuess]);
|
}, [text, setGuess]);
|
||||||
|
|
||||||
|
// Keeping track of the previous render's text length here
|
||||||
|
// to be able to reset the guess when the text length drops
|
||||||
|
// below the threshold needed to make a guess
|
||||||
|
const [wasLongText, setWasLongText] = useState(() =>
|
||||||
|
isTextLongEnoughForGuess(text),
|
||||||
|
);
|
||||||
|
if (wasLongText !== isTextLongEnoughForGuess(text)) {
|
||||||
|
setWasLongText(isTextLongEnoughForGuess(text));
|
||||||
|
|
||||||
|
if (wasLongText) {
|
||||||
|
setGuess('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={targetRef}>
|
<div ref={targetRef}>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useState, useRef, useEffect } from 'react';
|
import { useCallback, useState, useRef, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
defineMessages,
|
defineMessages,
|
||||||
@ -97,173 +97,13 @@ export const Search: React.FC<{
|
|||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const [selectedOption, setSelectedOption] = useState(-1);
|
const [selectedOption, setSelectedOption] = useState(-1);
|
||||||
const [quickActions, setQuickActions] = useState<SearchOption[]>([]);
|
const [quickActions, setQuickActions] = useState<SearchOption[]>([]);
|
||||||
useEffect(() => {
|
|
||||||
setValue(initialValue ?? '');
|
|
||||||
setQuickActions([]);
|
|
||||||
}, [initialValue]);
|
|
||||||
const searchOptions: SearchOption[] = [];
|
|
||||||
|
|
||||||
const unfocus = useCallback(() => {
|
const unfocus = useCallback(() => {
|
||||||
document.querySelector('.ui')?.parentElement?.focus();
|
document.querySelector('.ui')?.parentElement?.focus();
|
||||||
setExpanded(false);
|
setExpanded(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (searchEnabled) {
|
const insertText = useCallback((text: string) => {
|
||||||
searchOptions.push(
|
|
||||||
{
|
|
||||||
key: 'prompt-has',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>has:</mark>{' '}
|
|
||||||
<FormattedList
|
|
||||||
type='disjunction'
|
|
||||||
value={['media', 'poll', 'embed']}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('has:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-is',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>is:</mark>{' '}
|
|
||||||
<FormattedList type='disjunction' value={['reply', 'sensitive']} />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('is:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-language',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>language:</mark>{' '}
|
|
||||||
<FormattedMessage
|
|
||||||
id='search_popout.language_code'
|
|
||||||
defaultMessage='ISO language code'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('language:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-from',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>from:</mark>{' '}
|
|
||||||
<FormattedMessage id='search_popout.user' defaultMessage='user' />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('from:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-before',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>before:</mark>{' '}
|
|
||||||
<FormattedMessage
|
|
||||||
id='search_popout.specific_date'
|
|
||||||
defaultMessage='specific date'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('before:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-during',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>during:</mark>{' '}
|
|
||||||
<FormattedMessage
|
|
||||||
id='search_popout.specific_date'
|
|
||||||
defaultMessage='specific date'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('during:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-after',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>after:</mark>{' '}
|
|
||||||
<FormattedMessage
|
|
||||||
id='search_popout.specific_date'
|
|
||||||
defaultMessage='specific date'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('after:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'prompt-in',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<mark>in:</mark>{' '}
|
|
||||||
<FormattedList
|
|
||||||
type='disjunction'
|
|
||||||
value={['all', 'library', 'public']}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
action: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
insertText('in:');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const recentOptions: SearchOption[] = recent.map((search) => ({
|
|
||||||
key: `${search.type}/${search.q}`,
|
|
||||||
label: labelForRecentSearch(search),
|
|
||||||
action: () => {
|
|
||||||
setValue(search.q);
|
|
||||||
|
|
||||||
if (search.type === 'account') {
|
|
||||||
history.push(`/@${search.q}`);
|
|
||||||
} else if (search.type === 'hashtag') {
|
|
||||||
history.push(`/tags/${search.q}`);
|
|
||||||
} else {
|
|
||||||
const queryParams = new URLSearchParams({ q: search.q });
|
|
||||||
if (search.type) queryParams.set('type', search.type);
|
|
||||||
history.push({ pathname: '/search', search: queryParams.toString() });
|
|
||||||
}
|
|
||||||
|
|
||||||
unfocus();
|
|
||||||
},
|
|
||||||
forget: (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
void dispatch(forgetSearchResult(search));
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const navigableOptions = hasValue
|
|
||||||
? quickActions.concat(searchOptions)
|
|
||||||
: recentOptions.concat(quickActions, searchOptions);
|
|
||||||
|
|
||||||
const insertText = (text: string) => {
|
|
||||||
setValue((currentValue) => {
|
setValue((currentValue) => {
|
||||||
if (currentValue === '') {
|
if (currentValue === '') {
|
||||||
return text;
|
return text;
|
||||||
@ -273,7 +113,181 @@ export const Search: React.FC<{
|
|||||||
return `${currentValue} ${text}`;
|
return `${currentValue} ${text}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
|
const searchOptions = useMemo(() => {
|
||||||
|
if (!searchEnabled) {
|
||||||
|
return [];
|
||||||
|
} else {
|
||||||
|
const options: SearchOption[] = [
|
||||||
|
{
|
||||||
|
key: 'prompt-has',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>has:</mark>{' '}
|
||||||
|
<FormattedList
|
||||||
|
type='disjunction'
|
||||||
|
value={['media', 'poll', 'embed']}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('has:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-is',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>is:</mark>{' '}
|
||||||
|
<FormattedList
|
||||||
|
type='disjunction'
|
||||||
|
value={['reply', 'sensitive']}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('is:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-language',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>language:</mark>{' '}
|
||||||
|
<FormattedMessage
|
||||||
|
id='search_popout.language_code'
|
||||||
|
defaultMessage='ISO language code'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('language:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-from',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>from:</mark>{' '}
|
||||||
|
<FormattedMessage id='search_popout.user' defaultMessage='user' />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('from:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-before',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>before:</mark>{' '}
|
||||||
|
<FormattedMessage
|
||||||
|
id='search_popout.specific_date'
|
||||||
|
defaultMessage='specific date'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('before:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-during',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>during:</mark>{' '}
|
||||||
|
<FormattedMessage
|
||||||
|
id='search_popout.specific_date'
|
||||||
|
defaultMessage='specific date'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('during:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-after',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>after:</mark>{' '}
|
||||||
|
<FormattedMessage
|
||||||
|
id='search_popout.specific_date'
|
||||||
|
defaultMessage='specific date'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('after:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'prompt-in',
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<mark>in:</mark>{' '}
|
||||||
|
<FormattedList
|
||||||
|
type='disjunction'
|
||||||
|
value={['all', 'library', 'public']}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
action: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
insertText('in:');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}, [insertText]);
|
||||||
|
|
||||||
|
const recentOptions: SearchOption[] = useMemo(
|
||||||
|
() =>
|
||||||
|
recent.map((search) => ({
|
||||||
|
key: `${search.type}/${search.q}`,
|
||||||
|
label: labelForRecentSearch(search),
|
||||||
|
action: () => {
|
||||||
|
setValue(search.q);
|
||||||
|
|
||||||
|
if (search.type === 'account') {
|
||||||
|
history.push(`/@${search.q}`);
|
||||||
|
} else if (search.type === 'hashtag') {
|
||||||
|
history.push(`/tags/${search.q}`);
|
||||||
|
} else {
|
||||||
|
const queryParams = new URLSearchParams({ q: search.q });
|
||||||
|
if (search.type) queryParams.set('type', search.type);
|
||||||
|
history.push({
|
||||||
|
pathname: '/search',
|
||||||
|
search: queryParams.toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
unfocus();
|
||||||
|
},
|
||||||
|
forget: (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
void dispatch(forgetSearchResult(search));
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
[dispatch, history, recent, unfocus],
|
||||||
|
);
|
||||||
|
|
||||||
|
const navigableOptions: SearchOption[] = useMemo(
|
||||||
|
() =>
|
||||||
|
hasValue
|
||||||
|
? quickActions.concat(searchOptions)
|
||||||
|
: recentOptions.concat(quickActions, searchOptions),
|
||||||
|
[hasValue, quickActions, recentOptions, searchOptions],
|
||||||
|
);
|
||||||
|
|
||||||
const submit = useCallback(
|
const submit = useCallback(
|
||||||
(q: string, type?: SearchType) => {
|
(q: string, type?: SearchType) => {
|
||||||
|
|||||||
@ -19,14 +19,12 @@ const messages = defineMessages({
|
|||||||
const Blocks: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
|
const Blocks: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [domains, setDomains] = useState<string[]>([]);
|
const [domains, setDomains] = useState<string[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(true);
|
||||||
const [next, setNext] = useState<string | undefined>();
|
const [next, setNext] = useState<string | undefined>();
|
||||||
const hasMore = !!next;
|
const hasMore = !!next;
|
||||||
const columnRef = useRef<ColumnRef>(null);
|
const columnRef = useRef<ColumnRef>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
void apiGetDomainBlocks()
|
void apiGetDomainBlocks()
|
||||||
.then(({ domains, links }) => {
|
.then(({ domains, links }) => {
|
||||||
const next = links.refs.find((link) => link.rel === 'next');
|
const next = links.refs.find((link) => link.rel === 'next');
|
||||||
@ -40,7 +38,7 @@ const Blocks: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
|
|||||||
.catch(() => {
|
.catch(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}, [setLoading, setDomains, setNext]);
|
}, []);
|
||||||
|
|
||||||
const handleLoadMore = useCallback(() => {
|
const handleLoadMore = useCallback(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|||||||
@ -35,12 +35,17 @@ export const Announcement: FC<AnnouncementProps> = ({
|
|||||||
}, [active, id, dispatch, read]);
|
}, [active, id, dispatch, read]);
|
||||||
|
|
||||||
// But visually show the announcement as read only when it goes out of view.
|
// But visually show the announcement as read only when it goes out of view.
|
||||||
const [unread, setUnread] = useState(!read);
|
const [isVisuallyRead, setIsVisuallyRead] = useState(read);
|
||||||
useEffect(() => {
|
const [previousActive, setPreviousActive] = useState(active);
|
||||||
if (!active && unread !== !read) {
|
if (active !== previousActive) {
|
||||||
setUnread(!read);
|
setPreviousActive(active);
|
||||||
|
|
||||||
|
// This marks the announcement as read in the UI only after it
|
||||||
|
// went from active to inactive.
|
||||||
|
if (!active && isVisuallyRead !== read) {
|
||||||
|
setIsVisuallyRead(read);
|
||||||
}
|
}
|
||||||
}, [active, unread, read]);
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateEmojiProvider>
|
<AnimateEmojiProvider>
|
||||||
@ -63,7 +68,7 @@ export const Announcement: FC<AnnouncementProps> = ({
|
|||||||
|
|
||||||
<ReactionsBar reactions={announcement.reactions} id={announcement.id} />
|
<ReactionsBar reactions={announcement.reactions} id={announcement.id} />
|
||||||
|
|
||||||
{unread && <span className='announcements__unread' />}
|
{!isVisuallyRead && <span className='announcements__unread' />}
|
||||||
</AnimateEmojiProvider>
|
</AnimateEmojiProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -164,12 +164,11 @@ const ListMembers: React.FC<{
|
|||||||
const [searching, setSearching] = useState(false);
|
const [searching, setSearching] = useState(false);
|
||||||
const [accountIds, setAccountIds] = useState<string[]>([]);
|
const [accountIds, setAccountIds] = useState<string[]>([]);
|
||||||
const [searchAccountIds, setSearchAccountIds] = useState<string[]>([]);
|
const [searchAccountIds, setSearchAccountIds] = useState<string[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(!!id);
|
||||||
const [mode, setMode] = useState<Mode>('remove');
|
const [mode, setMode] = useState<Mode>('remove');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id) {
|
if (id) {
|
||||||
setLoading(true);
|
|
||||||
dispatch(fetchList(id));
|
dispatch(fetchList(id));
|
||||||
|
|
||||||
void apiGetAccounts(id)
|
void apiGetAccounts(id)
|
||||||
|
|||||||
@ -27,17 +27,15 @@ export const ListPanel: React.FC = () => {
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const lists = useAppSelector((state) => getOrderedLists(state));
|
const lists = useAppSelector((state) => getOrderedLists(state));
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
void dispatch(fetchLists()).then(() => {
|
void dispatch(fetchLists()).then(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
}, [dispatch, setLoading]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CollapsiblePanel
|
<CollapsiblePanel
|
||||||
|
|||||||
@ -225,7 +225,7 @@ export const SearchResults: React.FC<{ multiColumn: boolean }> = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='explore__search-header'>
|
<div className='explore__search-header'>
|
||||||
<Search singleColumn initialValue={trimmedValue} />
|
<Search singleColumn initialValue={trimmedValue} key={trimmedValue} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='account__section-headline'>
|
<div className='account__section-headline'>
|
||||||
|
|||||||
@ -295,7 +295,7 @@ export const RefreshController: React.FC<{
|
|||||||
if (loadingState === 'loading') {
|
if (loadingState === 'loading') {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='load-more load-gap'
|
className='load-more load-more--large'
|
||||||
aria-busy
|
aria-busy
|
||||||
aria-live='polite'
|
aria-live='polite'
|
||||||
aria-label={intl.formatMessage(messages.loadingInitial)}
|
aria-label={intl.formatMessage(messages.loadingInitial)}
|
||||||
|
|||||||
@ -53,8 +53,6 @@ export const DomainBlockModal: React.FC<{
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
apiRequest<DomainBlockPreviewResponse>('GET', 'v1/domain_blocks/preview', {
|
apiRequest<DomainBlockPreviewResponse>('GET', 'v1/domain_blocks/preview', {
|
||||||
params: { domain },
|
params: { domain },
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
@ -68,7 +66,7 @@ export const DomainBlockModal: React.FC<{
|
|||||||
setPreview('error');
|
setPreview('error');
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}, [setPreview, setLoading, domain]);
|
}, [domain]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='modal-root__modal safety-action-modal' aria-live='polite'>
|
<div className='modal-root__modal safety-action-modal' aria-live='polite'>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
import { useEffect, useState, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { useIntl, defineMessages } from 'react-intl';
|
import { useIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
@ -41,40 +41,44 @@ const isHashtagLink = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface TargetParams {
|
interface TargetParams {
|
||||||
hashtag?: string;
|
element: HTMLAnchorElement | null;
|
||||||
accountId?: string;
|
hashtag: string;
|
||||||
|
accountId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HashtagMenuController: React.FC = () => {
|
export const HashtagMenuController: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { signedIn } = useIdentity();
|
const { signedIn } = useIdentity();
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [{ accountId, hashtag }, setTargetParams] = useState<TargetParams>({});
|
const [target, setTarget] = useState<TargetParams | null>(null);
|
||||||
const targetRef = useRef<HTMLAnchorElement | null>(null);
|
const { element = null, accountId, hashtag } = target ?? {};
|
||||||
const location = useLocation();
|
const open = !!element;
|
||||||
|
|
||||||
const account = useAppSelector((state) =>
|
const account = useAppSelector((state) =>
|
||||||
accountId ? state.accounts.get(accountId) : undefined,
|
accountId ? state.accounts.get(accountId) : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
const location = useLocation();
|
||||||
setOpen(false);
|
const [previousLocation, setPreviousLocation] = useState(location);
|
||||||
targetRef.current = null;
|
if (location !== previousLocation) {
|
||||||
}, [setOpen, location]);
|
setPreviousLocation(location);
|
||||||
|
setTarget(null);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClick = (e: MouseEvent) => {
|
const handleClick = (e: MouseEvent) => {
|
||||||
const target = (e.target as HTMLElement).closest('a');
|
const targetElement = (e.target as HTMLElement).closest('a');
|
||||||
|
|
||||||
if (e.button !== 0 || e.ctrlKey || e.metaKey) {
|
if (e.button !== 0 || e.ctrlKey || e.metaKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isHashtagLink(target)) {
|
if (!isHashtagLink(targetElement)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashtag = target.text.replace(/^#/, '');
|
const hashtag = targetElement.text.replace(/^#/, '');
|
||||||
const accountId = target.getAttribute('data-menu-hashtag');
|
const accountId = targetElement.getAttribute('data-menu-hashtag');
|
||||||
|
|
||||||
if (!hashtag || !accountId) {
|
if (!hashtag || !accountId) {
|
||||||
return;
|
return;
|
||||||
@ -82,9 +86,7 @@ export const HashtagMenuController: React.FC = () => {
|
|||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
targetRef.current = target;
|
setTarget({ element: targetElement, hashtag, accountId });
|
||||||
setOpen(true);
|
|
||||||
setTargetParams({ hashtag, accountId });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('click', handleClick, { capture: true });
|
document.addEventListener('click', handleClick, { capture: true });
|
||||||
@ -92,12 +94,11 @@ export const HashtagMenuController: React.FC = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('click', handleClick);
|
document.removeEventListener('click', handleClick);
|
||||||
};
|
};
|
||||||
}, [setTargetParams, setOpen]);
|
}, []);
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setOpen(false);
|
setTarget(null);
|
||||||
targetRef.current = null;
|
}, []);
|
||||||
}, [setOpen]);
|
|
||||||
|
|
||||||
const menu = useMemo(() => {
|
const menu = useMemo(() => {
|
||||||
const arr: MenuItem[] = [
|
const arr: MenuItem[] = [
|
||||||
@ -139,7 +140,7 @@ export const HashtagMenuController: React.FC = () => {
|
|||||||
offset={offset}
|
offset={offset}
|
||||||
placement='bottom'
|
placement='bottom'
|
||||||
flip
|
flip
|
||||||
target={targetRef}
|
target={element}
|
||||||
popperConfig={popperConfig}
|
popperConfig={popperConfig}
|
||||||
>
|
>
|
||||||
{({ props, arrowProps, placement }) => (
|
{({ props, arrowProps, placement }) => (
|
||||||
|
|||||||
@ -66,6 +66,7 @@ export const MediaModal: FC<MediaModalProps> = forwardRef<
|
|||||||
_ref,
|
_ref,
|
||||||
) => {
|
) => {
|
||||||
const [index, setIndex] = useState(startIndex);
|
const [index, setIndex] = useState(startIndex);
|
||||||
|
const [zoomedIn, setZoomedIn] = useState(false);
|
||||||
const currentMedia = media.get(index);
|
const currentMedia = media.get(index);
|
||||||
|
|
||||||
const handleChangeIndex = useCallback(
|
const handleChangeIndex = useCallback(
|
||||||
@ -134,7 +135,6 @@ export const MediaModal: FC<MediaModalProps> = forwardRef<
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [zoomedIn, setZoomedIn] = useState(false);
|
|
||||||
const zoomable =
|
const zoomable =
|
||||||
currentMedia?.get('type') === 'image' &&
|
currentMedia?.get('type') === 'image' &&
|
||||||
((currentMedia.getIn(['meta', 'original', 'width']) as number) >
|
((currentMedia.getIn(['meta', 'original', 'width']) as number) >
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useSyncExternalStore } from 'react';
|
||||||
|
|
||||||
const breakpoints = {
|
const breakpoints = {
|
||||||
narrow: 479, // Device width under which horizontal space is constrained
|
narrow: 479, // Device width under which horizontal space is constrained
|
||||||
@ -9,25 +9,20 @@ const breakpoints = {
|
|||||||
type Breakpoint = keyof typeof breakpoints;
|
type Breakpoint = keyof typeof breakpoints;
|
||||||
|
|
||||||
export const useBreakpoint = (breakpoint: Breakpoint) => {
|
export const useBreakpoint = (breakpoint: Breakpoint) => {
|
||||||
const [isMatching, setIsMatching] = useState(false);
|
const query = `(max-width: ${breakpoints[breakpoint]}px)`;
|
||||||
|
|
||||||
useEffect(() => {
|
const isMatching = useSyncExternalStore(
|
||||||
const mediaWatcher = window.matchMedia(
|
(callback) => {
|
||||||
`(max-width: ${breakpoints[breakpoint]}px)`,
|
const mediaWatcher = window.matchMedia(query);
|
||||||
);
|
|
||||||
|
|
||||||
setIsMatching(mediaWatcher.matches);
|
mediaWatcher.addEventListener('change', callback);
|
||||||
|
|
||||||
const handleChange = (e: MediaQueryListEvent) => {
|
return () => {
|
||||||
setIsMatching(e.matches);
|
mediaWatcher.removeEventListener('change', callback);
|
||||||
};
|
};
|
||||||
|
},
|
||||||
mediaWatcher.addEventListener('change', handleChange);
|
() => window.matchMedia(query).matches,
|
||||||
|
);
|
||||||
return () => {
|
|
||||||
mediaWatcher.removeEventListener('change', handleChange);
|
|
||||||
};
|
|
||||||
}, [breakpoint, setIsMatching]);
|
|
||||||
|
|
||||||
return isMatching;
|
return isMatching;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useRef, useEffect } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the previous state of the passed in value.
|
* Returns the previous state of the passed in value.
|
||||||
@ -6,11 +6,21 @@ import { useRef, useEffect } from 'react';
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export function usePrevious<T>(value: T): T | undefined {
|
export function usePrevious<T>(value: T): T | undefined {
|
||||||
const ref = useRef<T>();
|
const [{ previous, current }, setMemory] = useState<{
|
||||||
|
previous: T | undefined;
|
||||||
|
current: T;
|
||||||
|
}>(() => ({ previous: undefined, current: value }));
|
||||||
|
|
||||||
useEffect(() => {
|
let result = previous;
|
||||||
ref.current = value;
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
return ref.current;
|
if (value !== current) {
|
||||||
|
setMemory({
|
||||||
|
previous: current,
|
||||||
|
current: value,
|
||||||
|
});
|
||||||
|
// Ensure that the returned result updates synchronously
|
||||||
|
result = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
"account.blocking": "Блакіраванне",
|
"account.blocking": "Блакіраванне",
|
||||||
"account.cancel_follow_request": "Скасаваць запыт на падпіску",
|
"account.cancel_follow_request": "Скасаваць запыт на падпіску",
|
||||||
"account.copy": "Скапіраваць спасылку на профіль",
|
"account.copy": "Скапіраваць спасылку на профіль",
|
||||||
"account.direct": "Згадаць асабіста @{name}",
|
"account.direct": "Згадаць прыватна @{name}",
|
||||||
"account.disable_notifications": "Не паведамляць мне пра публікацыі @{name}",
|
"account.disable_notifications": "Не паведамляць мне пра публікацыі @{name}",
|
||||||
"account.domain_blocking": "Блакіраванне дамена",
|
"account.domain_blocking": "Блакіраванне дамена",
|
||||||
"account.edit_profile": "Рэдагаваць профіль",
|
"account.edit_profile": "Рэдагаваць профіль",
|
||||||
@ -250,6 +250,10 @@
|
|||||||
"confirmations.missing_alt_text.title": "Дадаць альтэрнатыўны тэкст?",
|
"confirmations.missing_alt_text.title": "Дадаць альтэрнатыўны тэкст?",
|
||||||
"confirmations.mute.confirm": "Ігнараваць",
|
"confirmations.mute.confirm": "Ігнараваць",
|
||||||
"confirmations.private_quote_notify.cancel": "Звяртацца да рэдагавання",
|
"confirmations.private_quote_notify.cancel": "Звяртацца да рэдагавання",
|
||||||
|
"confirmations.private_quote_notify.confirm": "Апублікаваць допіс",
|
||||||
|
"confirmations.private_quote_notify.do_not_show_again": "Больш не паказваць мне гэтае паведамленне",
|
||||||
|
"confirmations.private_quote_notify.message": "Асоба, якую Вы цытуеце, і іншыя, хто быў узгаданы, атрымаюць апавяшчэнні і змогуць пабачыць Ваш допіс, нават калі яны не падпісаныя на Вас.",
|
||||||
|
"confirmations.private_quote_notify.title": "Падзяліцца з падпісчыкамі і ўзгаданымі карыстальнікамі?",
|
||||||
"confirmations.quiet_post_quote_info.dismiss": "Не нагадваць зноў",
|
"confirmations.quiet_post_quote_info.dismiss": "Не нагадваць зноў",
|
||||||
"confirmations.quiet_post_quote_info.got_it": "Зразумела",
|
"confirmations.quiet_post_quote_info.got_it": "Зразумела",
|
||||||
"confirmations.quiet_post_quote_info.message": "Калі будзеце цытаваць ціхі публічны допіс, Ваш допіс будзе схаваны ад трэндавых стужак.",
|
"confirmations.quiet_post_quote_info.message": "Калі будзеце цытаваць ціхі публічны допіс, Ваш допіс будзе схаваны ад трэндавых стужак.",
|
||||||
@ -761,6 +765,7 @@
|
|||||||
"privacy_policy.title": "Палітыка канфідэнцыйнасці",
|
"privacy_policy.title": "Палітыка канфідэнцыйнасці",
|
||||||
"quote_error.edit": "Нельга дадаваць цытаты пры рэдагаванні допісаў.",
|
"quote_error.edit": "Нельга дадаваць цытаты пры рэдагаванні допісаў.",
|
||||||
"quote_error.poll": "Нельга цытаваць з апытаннямі.",
|
"quote_error.poll": "Нельга цытаваць з апытаннямі.",
|
||||||
|
"quote_error.private_mentions": "Цытаванне не дазваляецца ў прамых узгадваннях.",
|
||||||
"quote_error.quote": "За раз дазволена рабіць толькі адну цытату.",
|
"quote_error.quote": "За раз дазволена рабіць толькі адну цытату.",
|
||||||
"quote_error.unauthorized": "Вы не ўвайшлі, каб цытаваць гэты допіс.",
|
"quote_error.unauthorized": "Вы не ўвайшлі, каб цытаваць гэты допіс.",
|
||||||
"quote_error.upload": "Нельга цытаваць з медыя далучэннямі.",
|
"quote_error.upload": "Нельга цытаваць з медыя далучэннямі.",
|
||||||
@ -1014,6 +1019,8 @@
|
|||||||
"video.volume_down": "Паменшыць гучнасць",
|
"video.volume_down": "Паменшыць гучнасць",
|
||||||
"video.volume_up": "Павялічыць гучнасць",
|
"video.volume_up": "Павялічыць гучнасць",
|
||||||
"visibility_modal.button_title": "Вызначыць бачнасць",
|
"visibility_modal.button_title": "Вызначыць бачнасць",
|
||||||
|
"visibility_modal.direct_quote_warning.text": "Калі Вы захавайце бягучыя налады, прымацаваная цытата будзе пераробленая ў спасылку.",
|
||||||
|
"visibility_modal.direct_quote_warning.title": "Цытаты нельга далучаць да прыватных узгадванняў",
|
||||||
"visibility_modal.header": "Бачнасць і ўзаемадзеянне",
|
"visibility_modal.header": "Бачнасць і ўзаемадзеянне",
|
||||||
"visibility_modal.helper.direct_quoting": "Прыватныя згадванні, створаныя на Mastodon, нельга цытаваць іншым людзям.",
|
"visibility_modal.helper.direct_quoting": "Прыватныя згадванні, створаныя на Mastodon, нельга цытаваць іншым людзям.",
|
||||||
"visibility_modal.helper.privacy_editing": "Бачнасць нельга змяніць у апублікаваным допісе.",
|
"visibility_modal.helper.privacy_editing": "Бачнасць нельга змяніць у апублікаваным допісе.",
|
||||||
|
|||||||
@ -249,6 +249,11 @@
|
|||||||
"confirmations.missing_alt_text.secondary": "Postio beth bynnag",
|
"confirmations.missing_alt_text.secondary": "Postio beth bynnag",
|
||||||
"confirmations.missing_alt_text.title": "Ychwanegu testun amgen?",
|
"confirmations.missing_alt_text.title": "Ychwanegu testun amgen?",
|
||||||
"confirmations.mute.confirm": "Tewi",
|
"confirmations.mute.confirm": "Tewi",
|
||||||
|
"confirmations.private_quote_notify.cancel": "Nôl i olygu",
|
||||||
|
"confirmations.private_quote_notify.confirm": "Cyhoeddi postiad",
|
||||||
|
"confirmations.private_quote_notify.do_not_show_again": "Peidio dangos y neges hon i mi eto",
|
||||||
|
"confirmations.private_quote_notify.message": "Bydd y person rydych chi'n ei ddyfynnu a chrybwylliadau eraill yn cael gwybod a bydd yn gallu gweld eich postiad, hyd yn oed os nad ydyn nhw'n eich dilyn chi.",
|
||||||
|
"confirmations.private_quote_notify.title": "Rhannu gyda dilynwyr a defnyddwyr sy'n cael eu crybwyll?",
|
||||||
"confirmations.quiet_post_quote_info.dismiss": "Peidio fy atgoff eto",
|
"confirmations.quiet_post_quote_info.dismiss": "Peidio fy atgoff eto",
|
||||||
"confirmations.quiet_post_quote_info.got_it": "Iawn",
|
"confirmations.quiet_post_quote_info.got_it": "Iawn",
|
||||||
"confirmations.quiet_post_quote_info.message": "Wrth ddyfynnu postiad cyhoeddus tawel, bydd eich postiad yn cael ei guddio rhag llinellau amser sy'n trendio.",
|
"confirmations.quiet_post_quote_info.message": "Wrth ddyfynnu postiad cyhoeddus tawel, bydd eich postiad yn cael ei guddio rhag llinellau amser sy'n trendio.",
|
||||||
@ -760,6 +765,7 @@
|
|||||||
"privacy_policy.title": "Polisi Preifatrwydd",
|
"privacy_policy.title": "Polisi Preifatrwydd",
|
||||||
"quote_error.edit": "Does dim modd ychwanegu dyfyniadau wrth olygu postiad.",
|
"quote_error.edit": "Does dim modd ychwanegu dyfyniadau wrth olygu postiad.",
|
||||||
"quote_error.poll": "Dyw dyfynnu ddim yn cael ei ganiatáu gyda pholau.",
|
"quote_error.poll": "Dyw dyfynnu ddim yn cael ei ganiatáu gyda pholau.",
|
||||||
|
"quote_error.private_mentions": "Does dim caniatâd i ddyfynnu gyda chrybwylliadau uniongyrchol.",
|
||||||
"quote_error.quote": "Dim ond un dyfyniad ar y tro sy'n cael ei ganiatáu.",
|
"quote_error.quote": "Dim ond un dyfyniad ar y tro sy'n cael ei ganiatáu.",
|
||||||
"quote_error.unauthorized": "Does gennych chi ddim awdurdod i ddyfynnu'r postiad hwn.",
|
"quote_error.unauthorized": "Does gennych chi ddim awdurdod i ddyfynnu'r postiad hwn.",
|
||||||
"quote_error.upload": "Dyw dyfynnu ddim yn cael ei ganiatáu gydag atodiadau cyfryngau.",
|
"quote_error.upload": "Dyw dyfynnu ddim yn cael ei ganiatáu gydag atodiadau cyfryngau.",
|
||||||
@ -1013,6 +1019,8 @@
|
|||||||
"video.volume_down": "Lefel sain i lawr",
|
"video.volume_down": "Lefel sain i lawr",
|
||||||
"video.volume_up": "Lefel sain i fyny",
|
"video.volume_up": "Lefel sain i fyny",
|
||||||
"visibility_modal.button_title": "Gosod gwelededd",
|
"visibility_modal.button_title": "Gosod gwelededd",
|
||||||
|
"visibility_modal.direct_quote_warning.text": "Os byddwch chi'n cadw'r gosodiadau cyfredol, bydd y dyfyniad sydd wedi'i fewnosod yn cael ei drawsnewid yn ddolen.",
|
||||||
|
"visibility_modal.direct_quote_warning.title": "Does dim modd mewnblannu dyfyniadau mewn crybwylliadau preifat",
|
||||||
"visibility_modal.header": "Gwelededd a rhyngweithio",
|
"visibility_modal.header": "Gwelededd a rhyngweithio",
|
||||||
"visibility_modal.helper.direct_quoting": "Does dim modd dyfynnu crybwylliadau preifat ysgrifennwyd ar Mastodon.",
|
"visibility_modal.helper.direct_quoting": "Does dim modd dyfynnu crybwylliadau preifat ysgrifennwyd ar Mastodon.",
|
||||||
"visibility_modal.helper.privacy_editing": "Does dim modd newid gwelededd ar ôl i bostiad gael ei gyhoeddi.",
|
"visibility_modal.helper.privacy_editing": "Does dim modd newid gwelededd ar ôl i bostiad gael ei gyhoeddi.",
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
"account.follow_back": "Ebenfalls folgen",
|
"account.follow_back": "Ebenfalls folgen",
|
||||||
"account.follow_back_short": "Ebenfalls folgen",
|
"account.follow_back_short": "Ebenfalls folgen",
|
||||||
"account.follow_request": "Anfrage zum Folgen",
|
"account.follow_request": "Anfrage zum Folgen",
|
||||||
"account.follow_request_cancel": "Anfrage zurückziehen",
|
"account.follow_request_cancel": "Anfrage abbrechen",
|
||||||
"account.follow_request_cancel_short": "Abbrechen",
|
"account.follow_request_cancel_short": "Abbrechen",
|
||||||
"account.follow_request_short": "Anfragen",
|
"account.follow_request_short": "Anfragen",
|
||||||
"account.followers": "Follower",
|
"account.followers": "Follower",
|
||||||
@ -272,7 +272,7 @@
|
|||||||
"confirmations.unfollow.confirm": "Entfolgen",
|
"confirmations.unfollow.confirm": "Entfolgen",
|
||||||
"confirmations.unfollow.title": "{name} entfolgen?",
|
"confirmations.unfollow.title": "{name} entfolgen?",
|
||||||
"confirmations.withdraw_request.confirm": "Anfrage zurückziehen",
|
"confirmations.withdraw_request.confirm": "Anfrage zurückziehen",
|
||||||
"confirmations.withdraw_request.title": "Anfrage zum Folgen von {name} zurückziehen?",
|
"confirmations.withdraw_request.title": "Anfrage zum Folgen von {name} widerrufen?",
|
||||||
"content_warning.hide": "Beitrag ausblenden",
|
"content_warning.hide": "Beitrag ausblenden",
|
||||||
"content_warning.show": "Trotzdem anzeigen",
|
"content_warning.show": "Trotzdem anzeigen",
|
||||||
"content_warning.show_more": "Beitrag anzeigen",
|
"content_warning.show_more": "Beitrag anzeigen",
|
||||||
@ -882,7 +882,7 @@
|
|||||||
"status.block": "@{name} blockieren",
|
"status.block": "@{name} blockieren",
|
||||||
"status.bookmark": "Lesezeichen setzen",
|
"status.bookmark": "Lesezeichen setzen",
|
||||||
"status.cancel_reblog_private": "Beitrag nicht mehr teilen",
|
"status.cancel_reblog_private": "Beitrag nicht mehr teilen",
|
||||||
"status.cannot_quote": "Dir ist es nicht gestattet, diesen Beitrag zu zitieren",
|
"status.cannot_quote": "Beitrag kann nicht zitiert werden",
|
||||||
"status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden",
|
"status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden",
|
||||||
"status.contains_quote": "Enthält Zitat",
|
"status.contains_quote": "Enthält Zitat",
|
||||||
"status.context.loading": "Weitere Antworten laden",
|
"status.context.loading": "Weitere Antworten laden",
|
||||||
@ -933,7 +933,7 @@
|
|||||||
"status.quote_manual_review": "Zitierte*r überprüft manuell",
|
"status.quote_manual_review": "Zitierte*r überprüft manuell",
|
||||||
"status.quote_noun": "Zitat",
|
"status.quote_noun": "Zitat",
|
||||||
"status.quote_policy_change": "Ändern, wer zitieren darf",
|
"status.quote_policy_change": "Ändern, wer zitieren darf",
|
||||||
"status.quote_post_author": "Zitierte einen Beitrag von @{name}",
|
"status.quote_post_author": "Zitierter Beitrag von @{name}",
|
||||||
"status.quote_private": "Private Beiträge können nicht zitiert werden",
|
"status.quote_private": "Private Beiträge können nicht zitiert werden",
|
||||||
"status.quotes": "{count, plural, one {Mal zitiert} other {Mal zitiert}}",
|
"status.quotes": "{count, plural, one {Mal zitiert} other {Mal zitiert}}",
|
||||||
"status.quotes.empty": "Diesen Beitrag hat bisher noch niemand zitiert. Sobald es jemand tut, wird das Profil hier erscheinen.",
|
"status.quotes.empty": "Diesen Beitrag hat bisher noch niemand zitiert. Sobald es jemand tut, wird das Profil hier erscheinen.",
|
||||||
@ -1021,7 +1021,7 @@
|
|||||||
"visibility_modal.button_title": "Sichtbarkeit festlegen",
|
"visibility_modal.button_title": "Sichtbarkeit festlegen",
|
||||||
"visibility_modal.direct_quote_warning.text": "Wenn diese Einstellungen gespeichert werden, wird das eingebettete Zitat in einen Link umgewandelt.",
|
"visibility_modal.direct_quote_warning.text": "Wenn diese Einstellungen gespeichert werden, wird das eingebettete Zitat in einen Link umgewandelt.",
|
||||||
"visibility_modal.direct_quote_warning.title": "Zitate können in privaten Erwähnungen nicht eingebettet werden",
|
"visibility_modal.direct_quote_warning.title": "Zitate können in privaten Erwähnungen nicht eingebettet werden",
|
||||||
"visibility_modal.header": "Sichtbarkeit und Interaktion",
|
"visibility_modal.header": "Sichtbarkeit und Zitate",
|
||||||
"visibility_modal.helper.direct_quoting": "Private Erwähnungen, die auf Mastodon verfasst wurden, können nicht von anderen zitiert werden.",
|
"visibility_modal.helper.direct_quoting": "Private Erwähnungen, die auf Mastodon verfasst wurden, können nicht von anderen zitiert werden.",
|
||||||
"visibility_modal.helper.privacy_editing": "Die Sichtbarkeit eines bereits veröffentlichten Beitrags kann nachträglich nicht mehr geändert werden.",
|
"visibility_modal.helper.privacy_editing": "Die Sichtbarkeit eines bereits veröffentlichten Beitrags kann nachträglich nicht mehr geändert werden.",
|
||||||
"visibility_modal.helper.privacy_private_self_quote": "Beiträge mit privaten Erwähnungen können öffentlich nicht zitiert werden.",
|
"visibility_modal.helper.privacy_private_self_quote": "Beiträge mit privaten Erwähnungen können öffentlich nicht zitiert werden.",
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
"account.disable_notifications": "Stop notifying me when @{name} posts",
|
"account.disable_notifications": "Stop notifying me when @{name} posts",
|
||||||
"account.domain_blocking": "Blocking domain",
|
"account.domain_blocking": "Blocking domain",
|
||||||
"account.edit_profile": "Edit profile",
|
"account.edit_profile": "Edit profile",
|
||||||
|
"account.edit_profile_short": "Edit",
|
||||||
"account.enable_notifications": "Notify me when @{name} posts",
|
"account.enable_notifications": "Notify me when @{name} posts",
|
||||||
"account.endorse": "Feature on profile",
|
"account.endorse": "Feature on profile",
|
||||||
"account.familiar_followers_many": "Followed by {name1}, {name2}, and {othersCount, plural, one {one other you know} other {# others you know}}",
|
"account.familiar_followers_many": "Followed by {name1}, {name2}, and {othersCount, plural, one {one other you know} other {# others you know}}",
|
||||||
@ -40,6 +41,11 @@
|
|||||||
"account.featured_tags.last_status_never": "No posts",
|
"account.featured_tags.last_status_never": "No posts",
|
||||||
"account.follow": "Follow",
|
"account.follow": "Follow",
|
||||||
"account.follow_back": "Follow back",
|
"account.follow_back": "Follow back",
|
||||||
|
"account.follow_back_short": "Follow back",
|
||||||
|
"account.follow_request": "Request to follow",
|
||||||
|
"account.follow_request_cancel": "Cancel request",
|
||||||
|
"account.follow_request_cancel_short": "Cancel",
|
||||||
|
"account.follow_request_short": "Request",
|
||||||
"account.followers": "Followers",
|
"account.followers": "Followers",
|
||||||
"account.followers.empty": "No one follows this user yet.",
|
"account.followers.empty": "No one follows this user yet.",
|
||||||
"account.followers_counter": "{count, plural, one {{counter} follower} other {{counter} followers}}",
|
"account.followers_counter": "{count, plural, one {{counter} follower} other {{counter} followers}}",
|
||||||
@ -151,6 +157,8 @@
|
|||||||
"bundle_modal_error.close": "Close",
|
"bundle_modal_error.close": "Close",
|
||||||
"bundle_modal_error.message": "Something went wrong while loading this screen.",
|
"bundle_modal_error.message": "Something went wrong while loading this screen.",
|
||||||
"bundle_modal_error.retry": "Try again",
|
"bundle_modal_error.retry": "Try again",
|
||||||
|
"carousel.current": "<sr>Slide</sr> {current, number} / {max, number}",
|
||||||
|
"carousel.slide": "Slide {current, number} of {max, number}",
|
||||||
"closed_registrations.other_server_instructions": "Since Mastodon is decentralised, you can create an account on another server and still interact with this one.",
|
"closed_registrations.other_server_instructions": "Since Mastodon is decentralised, you can create an account on another server and still interact with this one.",
|
||||||
"closed_registrations_modal.description": "Creating an account on {domain} is currently not possible, but please keep in mind that you do not need an account specifically on {domain} to use Mastodon.",
|
"closed_registrations_modal.description": "Creating an account on {domain} is currently not possible, but please keep in mind that you do not need an account specifically on {domain} to use Mastodon.",
|
||||||
"closed_registrations_modal.find_another_server": "Find another server",
|
"closed_registrations_modal.find_another_server": "Find another server",
|
||||||
@ -167,6 +175,8 @@
|
|||||||
"column.edit_list": "Edit list",
|
"column.edit_list": "Edit list",
|
||||||
"column.favourites": "Favourites",
|
"column.favourites": "Favourites",
|
||||||
"column.firehose": "Live feeds",
|
"column.firehose": "Live feeds",
|
||||||
|
"column.firehose_local": "Live feed for this server",
|
||||||
|
"column.firehose_singular": "Live feed",
|
||||||
"column.follow_requests": "Follow requests",
|
"column.follow_requests": "Follow requests",
|
||||||
"column.home": "Home",
|
"column.home": "Home",
|
||||||
"column.list_members": "Manage list members",
|
"column.list_members": "Manage list members",
|
||||||
@ -186,6 +196,7 @@
|
|||||||
"community.column_settings.local_only": "Local only",
|
"community.column_settings.local_only": "Local only",
|
||||||
"community.column_settings.media_only": "Media Only",
|
"community.column_settings.media_only": "Media Only",
|
||||||
"community.column_settings.remote_only": "Remote only",
|
"community.column_settings.remote_only": "Remote only",
|
||||||
|
"compose.error.blank_post": "Post can't be blank.",
|
||||||
"compose.language.change": "Change language",
|
"compose.language.change": "Change language",
|
||||||
"compose.language.search": "Search languages...",
|
"compose.language.search": "Search languages...",
|
||||||
"compose.published.body": "Post published.",
|
"compose.published.body": "Post published.",
|
||||||
@ -238,13 +249,30 @@
|
|||||||
"confirmations.missing_alt_text.secondary": "Post anyway",
|
"confirmations.missing_alt_text.secondary": "Post anyway",
|
||||||
"confirmations.missing_alt_text.title": "Add alt text?",
|
"confirmations.missing_alt_text.title": "Add alt text?",
|
||||||
"confirmations.mute.confirm": "Mute",
|
"confirmations.mute.confirm": "Mute",
|
||||||
|
"confirmations.private_quote_notify.cancel": "Back to editing",
|
||||||
|
"confirmations.private_quote_notify.confirm": "Publish post",
|
||||||
|
"confirmations.private_quote_notify.do_not_show_again": "Don't show me this message again",
|
||||||
|
"confirmations.private_quote_notify.message": "The person you are quoting and other mentions will be notified and will be able to view your post, even if they're not following you.",
|
||||||
|
"confirmations.private_quote_notify.title": "Share with followers and mentioned users?",
|
||||||
|
"confirmations.quiet_post_quote_info.dismiss": "Don't remind me again",
|
||||||
|
"confirmations.quiet_post_quote_info.got_it": "Got it",
|
||||||
|
"confirmations.quiet_post_quote_info.message": "When quoting a quiet public post, your post will be hidden from trending timelines.",
|
||||||
|
"confirmations.quiet_post_quote_info.title": "Quoting quiet public posts",
|
||||||
"confirmations.redraft.confirm": "Delete & redraft",
|
"confirmations.redraft.confirm": "Delete & redraft",
|
||||||
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
|
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
|
||||||
"confirmations.redraft.title": "Delete & redraft post?",
|
"confirmations.redraft.title": "Delete & redraft post?",
|
||||||
"confirmations.remove_from_followers.confirm": "Remove follower",
|
"confirmations.remove_from_followers.confirm": "Remove follower",
|
||||||
"confirmations.remove_from_followers.message": "{name} will stop following you. Are you sure you want to proceed?",
|
"confirmations.remove_from_followers.message": "{name} will stop following you. Are you sure you want to proceed?",
|
||||||
"confirmations.remove_from_followers.title": "Remove follower?",
|
"confirmations.remove_from_followers.title": "Remove follower?",
|
||||||
|
"confirmations.revoke_quote.confirm": "Remove post",
|
||||||
|
"confirmations.revoke_quote.message": "This action cannot be undone.",
|
||||||
|
"confirmations.revoke_quote.title": "Remove post?",
|
||||||
|
"confirmations.unblock.confirm": "Unblock",
|
||||||
|
"confirmations.unblock.title": "Unblock {name}?",
|
||||||
"confirmations.unfollow.confirm": "Unfollow",
|
"confirmations.unfollow.confirm": "Unfollow",
|
||||||
|
"confirmations.unfollow.title": "Unfollow {name}?",
|
||||||
|
"confirmations.withdraw_request.confirm": "Withdraw request",
|
||||||
|
"confirmations.withdraw_request.title": "Withdraw request to follow {name}?",
|
||||||
"content_warning.hide": "Hide post",
|
"content_warning.hide": "Hide post",
|
||||||
"content_warning.show": "Show anyway",
|
"content_warning.show": "Show anyway",
|
||||||
"content_warning.show_more": "Show more",
|
"content_warning.show_more": "Show more",
|
||||||
@ -286,6 +314,7 @@
|
|||||||
"domain_pill.your_handle": "Your handle:",
|
"domain_pill.your_handle": "Your handle:",
|
||||||
"domain_pill.your_server": "Your digital home, where all of your posts live. Don’t like this one? Transfer servers at any time and bring your followers, too.",
|
"domain_pill.your_server": "Your digital home, where all of your posts live. Don’t like this one? Transfer servers at any time and bring your followers, too.",
|
||||||
"domain_pill.your_username": "Your unique identifier on this server. It’s possible to find users with the same username on different servers.",
|
"domain_pill.your_username": "Your unique identifier on this server. It’s possible to find users with the same username on different servers.",
|
||||||
|
"dropdown.empty": "Select an option",
|
||||||
"embed.instructions": "Embed this post on your website by copying the code below.",
|
"embed.instructions": "Embed this post on your website by copying the code below.",
|
||||||
"embed.preview": "Here is what it will look like:",
|
"embed.preview": "Here is what it will look like:",
|
||||||
"emoji_button.activity": "Activity",
|
"emoji_button.activity": "Activity",
|
||||||
@ -314,6 +343,7 @@
|
|||||||
"empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
|
"empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
|
||||||
"empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
|
"empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
|
||||||
"empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
|
"empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
|
||||||
|
"empty_column.disabled_feed": "This feed has been disabled by your server administrators.",
|
||||||
"empty_column.domain_blocks": "There are no blocked domains yet.",
|
"empty_column.domain_blocks": "There are no blocked domains yet.",
|
||||||
"empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
|
"empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
|
||||||
"empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
|
"empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
|
||||||
@ -338,7 +368,9 @@
|
|||||||
"explore.trending_links": "News",
|
"explore.trending_links": "News",
|
||||||
"explore.trending_statuses": "Posts",
|
"explore.trending_statuses": "Posts",
|
||||||
"explore.trending_tags": "Hashtags",
|
"explore.trending_tags": "Hashtags",
|
||||||
|
"featured_carousel.current": "<sr>Post</sr> {current, number} / {max, number}",
|
||||||
"featured_carousel.header": "{count, plural, one {Pinned Post} other {Pinned Posts}}",
|
"featured_carousel.header": "{count, plural, one {Pinned Post} other {Pinned Posts}}",
|
||||||
|
"featured_carousel.slide": "Post {current, number} of {max, number}",
|
||||||
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.",
|
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.",
|
||||||
"filter_modal.added.context_mismatch_title": "Context mismatch!",
|
"filter_modal.added.context_mismatch_title": "Context mismatch!",
|
||||||
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.",
|
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.",
|
||||||
@ -381,6 +413,7 @@
|
|||||||
"follow_suggestions.who_to_follow": "Who to follow",
|
"follow_suggestions.who_to_follow": "Who to follow",
|
||||||
"followed_tags": "Followed hashtags",
|
"followed_tags": "Followed hashtags",
|
||||||
"footer.about": "About",
|
"footer.about": "About",
|
||||||
|
"footer.about_this_server": "About",
|
||||||
"footer.directory": "Profiles directory",
|
"footer.directory": "Profiles directory",
|
||||||
"footer.get_app": "Get the app",
|
"footer.get_app": "Get the app",
|
||||||
"footer.keyboard_shortcuts": "Keyboard shortcuts",
|
"footer.keyboard_shortcuts": "Keyboard shortcuts",
|
||||||
@ -438,10 +471,12 @@
|
|||||||
"ignore_notifications_modal.private_mentions_title": "Ignore notifications from unsolicited Private Mentions?",
|
"ignore_notifications_modal.private_mentions_title": "Ignore notifications from unsolicited Private Mentions?",
|
||||||
"info_button.label": "Help",
|
"info_button.label": "Help",
|
||||||
"info_button.what_is_alt_text": "<h1>What is alt text?</h1> <p>Alt text provides image descriptions for people with vision impairments, low-bandwidth connections, or those seeking extra context.</p> <p>You can improve accessibility and understanding for everyone by writing clear, concise, and objective alt text.</p> <ul> <li>Capture important elements</li> <li>Summarise text in images</li> <li>Use regular sentence structure</li> <li>Avoid redundant information</li> <li>Focus on trends and key findings in complex visuals (like diagrams or maps)</li> </ul>",
|
"info_button.what_is_alt_text": "<h1>What is alt text?</h1> <p>Alt text provides image descriptions for people with vision impairments, low-bandwidth connections, or those seeking extra context.</p> <p>You can improve accessibility and understanding for everyone by writing clear, concise, and objective alt text.</p> <ul> <li>Capture important elements</li> <li>Summarise text in images</li> <li>Use regular sentence structure</li> <li>Avoid redundant information</li> <li>Focus on trends and key findings in complex visuals (like diagrams or maps)</li> </ul>",
|
||||||
|
"interaction_modal.action": "To interact with {name}'s post, you need to sign into your account on whatever Mastodon server you use.",
|
||||||
"interaction_modal.go": "Go",
|
"interaction_modal.go": "Go",
|
||||||
"interaction_modal.no_account_yet": "Don't have an account yet?",
|
"interaction_modal.no_account_yet": "Don't have an account yet?",
|
||||||
"interaction_modal.on_another_server": "On a different server",
|
"interaction_modal.on_another_server": "On a different server",
|
||||||
"interaction_modal.on_this_server": "On this server",
|
"interaction_modal.on_this_server": "On this server",
|
||||||
|
"interaction_modal.title": "Sign in to continue",
|
||||||
"interaction_modal.username_prompt": "E.g. {example}",
|
"interaction_modal.username_prompt": "E.g. {example}",
|
||||||
"intervals.full.days": "{number, plural, one {# day} other {# days}}",
|
"intervals.full.days": "{number, plural, one {# day} other {# days}}",
|
||||||
"intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
|
"intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
|
||||||
@ -462,6 +497,7 @@
|
|||||||
"keyboard_shortcuts.home": "Open home timeline",
|
"keyboard_shortcuts.home": "Open home timeline",
|
||||||
"keyboard_shortcuts.hotkey": "Hotkey",
|
"keyboard_shortcuts.hotkey": "Hotkey",
|
||||||
"keyboard_shortcuts.legend": "to display this legend",
|
"keyboard_shortcuts.legend": "to display this legend",
|
||||||
|
"keyboard_shortcuts.load_more": "Focus \"Load more\" button",
|
||||||
"keyboard_shortcuts.local": "to open local timeline",
|
"keyboard_shortcuts.local": "to open local timeline",
|
||||||
"keyboard_shortcuts.mention": "to mention author",
|
"keyboard_shortcuts.mention": "to mention author",
|
||||||
"keyboard_shortcuts.muted": "to open muted users list",
|
"keyboard_shortcuts.muted": "to open muted users list",
|
||||||
@ -470,6 +506,7 @@
|
|||||||
"keyboard_shortcuts.open_media": "to open media",
|
"keyboard_shortcuts.open_media": "to open media",
|
||||||
"keyboard_shortcuts.pinned": "to open pinned posts list",
|
"keyboard_shortcuts.pinned": "to open pinned posts list",
|
||||||
"keyboard_shortcuts.profile": "to open author's profile",
|
"keyboard_shortcuts.profile": "to open author's profile",
|
||||||
|
"keyboard_shortcuts.quote": "Quote post",
|
||||||
"keyboard_shortcuts.reply": "to reply",
|
"keyboard_shortcuts.reply": "to reply",
|
||||||
"keyboard_shortcuts.requests": "to open follow requests list",
|
"keyboard_shortcuts.requests": "to open follow requests list",
|
||||||
"keyboard_shortcuts.search": "to focus search",
|
"keyboard_shortcuts.search": "to focus search",
|
||||||
@ -481,6 +518,8 @@
|
|||||||
"keyboard_shortcuts.translate": "to translate a post",
|
"keyboard_shortcuts.translate": "to translate a post",
|
||||||
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
|
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
|
||||||
"keyboard_shortcuts.up": "Move up in the list",
|
"keyboard_shortcuts.up": "Move up in the list",
|
||||||
|
"learn_more_link.got_it": "Got it",
|
||||||
|
"learn_more_link.learn_more": "Learn more",
|
||||||
"lightbox.close": "Close",
|
"lightbox.close": "Close",
|
||||||
"lightbox.next": "Next",
|
"lightbox.next": "Next",
|
||||||
"lightbox.previous": "Previous",
|
"lightbox.previous": "Previous",
|
||||||
@ -581,6 +620,7 @@
|
|||||||
"notification.label.mention": "Mention",
|
"notification.label.mention": "Mention",
|
||||||
"notification.label.private_mention": "Private mention",
|
"notification.label.private_mention": "Private mention",
|
||||||
"notification.label.private_reply": "Private reply",
|
"notification.label.private_reply": "Private reply",
|
||||||
|
"notification.label.quote": "{name} quoted your post",
|
||||||
"notification.label.reply": "Reply",
|
"notification.label.reply": "Reply",
|
||||||
"notification.mention": "Mention",
|
"notification.mention": "Mention",
|
||||||
"notification.mentioned_you": "{name} mentioned you",
|
"notification.mentioned_you": "{name} mentioned you",
|
||||||
@ -595,6 +635,7 @@
|
|||||||
"notification.moderation_warning.action_suspend": "Your account has been suspended.",
|
"notification.moderation_warning.action_suspend": "Your account has been suspended.",
|
||||||
"notification.own_poll": "Your poll has ended",
|
"notification.own_poll": "Your poll has ended",
|
||||||
"notification.poll": "A poll you voted in has ended",
|
"notification.poll": "A poll you voted in has ended",
|
||||||
|
"notification.quoted_update": "{name} edited a post you have quoted",
|
||||||
"notification.reblog": "{name} boosted your post",
|
"notification.reblog": "{name} boosted your post",
|
||||||
"notification.reblog.name_and_others_with_link": "{name} and <a>{count, plural, one {# other} other {# others}}</a> boosted your post",
|
"notification.reblog.name_and_others_with_link": "{name} and <a>{count, plural, one {# other} other {# others}}</a> boosted your post",
|
||||||
"notification.relationships_severance_event": "Lost connections with {name}",
|
"notification.relationships_severance_event": "Lost connections with {name}",
|
||||||
@ -638,6 +679,7 @@
|
|||||||
"notifications.column_settings.mention": "Mentions:",
|
"notifications.column_settings.mention": "Mentions:",
|
||||||
"notifications.column_settings.poll": "Poll results:",
|
"notifications.column_settings.poll": "Poll results:",
|
||||||
"notifications.column_settings.push": "Push notifications",
|
"notifications.column_settings.push": "Push notifications",
|
||||||
|
"notifications.column_settings.quote": "Quotes:",
|
||||||
"notifications.column_settings.reblog": "Boosts:",
|
"notifications.column_settings.reblog": "Boosts:",
|
||||||
"notifications.column_settings.show": "Show in column",
|
"notifications.column_settings.show": "Show in column",
|
||||||
"notifications.column_settings.sound": "Play sound",
|
"notifications.column_settings.sound": "Play sound",
|
||||||
@ -713,10 +755,18 @@
|
|||||||
"privacy.private.short": "Followers",
|
"privacy.private.short": "Followers",
|
||||||
"privacy.public.long": "Anyone on and off Mastodon",
|
"privacy.public.long": "Anyone on and off Mastodon",
|
||||||
"privacy.public.short": "Public",
|
"privacy.public.short": "Public",
|
||||||
|
"privacy.quote.anyone": "{visibility}, anyone can quote",
|
||||||
|
"privacy.quote.disabled": "{visibility}, quotes disabled",
|
||||||
|
"privacy.quote.limited": "{visibility}, quotes limited",
|
||||||
"privacy.unlisted.additional": "This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.",
|
"privacy.unlisted.additional": "This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.",
|
||||||
|
"privacy.unlisted.long": "Hidden from Mastodon search results, trending, and public timelines",
|
||||||
"privacy.unlisted.short": "Quiet public",
|
"privacy.unlisted.short": "Quiet public",
|
||||||
"privacy_policy.last_updated": "Last updated {date}",
|
"privacy_policy.last_updated": "Last updated {date}",
|
||||||
"privacy_policy.title": "Privacy Policy",
|
"privacy_policy.title": "Privacy Policy",
|
||||||
|
"quote_error.edit": "Quotes cannot be added when editing a post.",
|
||||||
|
"quote_error.poll": "Quoting is not allowed with polls.",
|
||||||
|
"quote_error.private_mentions": "Quoting is not allowed with direct mentions.",
|
||||||
|
"quote_error.quote": "Only one quote at a time is allowed.",
|
||||||
"recommended": "Recommended",
|
"recommended": "Recommended",
|
||||||
"refresh": "Refresh",
|
"refresh": "Refresh",
|
||||||
"regeneration_indicator.please_stand_by": "Please stand by.",
|
"regeneration_indicator.please_stand_by": "Please stand by.",
|
||||||
@ -925,5 +975,13 @@
|
|||||||
"video.skip_forward": "Skip forward",
|
"video.skip_forward": "Skip forward",
|
||||||
"video.unmute": "Unmute",
|
"video.unmute": "Unmute",
|
||||||
"video.volume_down": "Volume down",
|
"video.volume_down": "Volume down",
|
||||||
"video.volume_up": "Volume up"
|
"video.volume_up": "Volume up",
|
||||||
|
"visibility_modal.helper.unlisted_quoting": "When people quote you, their post will also be hidden from trending timelines.",
|
||||||
|
"visibility_modal.instructions": "Control who can interact with this post. You can also apply settings to all future posts by navigating to <link>Preferences > Posting defaults</link>.",
|
||||||
|
"visibility_modal.privacy_label": "Visibility",
|
||||||
|
"visibility_modal.quote_followers": "Followers only",
|
||||||
|
"visibility_modal.quote_label": "Who can quote",
|
||||||
|
"visibility_modal.quote_nobody": "Just me",
|
||||||
|
"visibility_modal.quote_public": "Anyone",
|
||||||
|
"visibility_modal.save": "Save"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -742,10 +742,10 @@
|
|||||||
"poll.refresh": "Refrescar",
|
"poll.refresh": "Refrescar",
|
||||||
"poll.reveal": "Ver resultados",
|
"poll.reveal": "Ver resultados",
|
||||||
"poll.total_people": "{count, plural, one {# persona} other {# personas}}",
|
"poll.total_people": "{count, plural, one {# persona} other {# personas}}",
|
||||||
"poll.total_votes": "{count, plural, one {# voto} other {# votos}}",
|
"poll.total_votes": "{count, plural, one {voto} other {votos}}",
|
||||||
"poll.vote": "Votar",
|
"poll.vote": "Votar",
|
||||||
"poll.voted": "Votaste esta opción",
|
"poll.voted": "Votaste esta opción",
|
||||||
"poll.votes": "{votes, plural, one {# voto} other {# votos}}",
|
"poll.votes": "{votes, plural, one {voto} other {votos}}",
|
||||||
"poll_button.add_poll": "Agregar encuesta",
|
"poll_button.add_poll": "Agregar encuesta",
|
||||||
"poll_button.remove_poll": "Quitar encuesta",
|
"poll_button.remove_poll": "Quitar encuesta",
|
||||||
"privacy.change": "Configurar privacidad del mensaje",
|
"privacy.change": "Configurar privacidad del mensaje",
|
||||||
@ -760,7 +760,7 @@
|
|||||||
"privacy.quote.limited": "{visibility}, citas limitadas",
|
"privacy.quote.limited": "{visibility}, citas limitadas",
|
||||||
"privacy.unlisted.additional": "Esto se comporta exactamente igual que con la configuración de privacidad de mensaje «Público», excepto que el mensaje no aparecerá en las líneas temporales en vivo, ni en las etiquetas, ni en la línea temporal «Explorá», ni en la búsqueda de Mastodon; incluso si optaste por hacer tu cuenta visible.",
|
"privacy.unlisted.additional": "Esto se comporta exactamente igual que con la configuración de privacidad de mensaje «Público», excepto que el mensaje no aparecerá en las líneas temporales en vivo, ni en las etiquetas, ni en la línea temporal «Explorá», ni en la búsqueda de Mastodon; incluso si optaste por hacer tu cuenta visible.",
|
||||||
"privacy.unlisted.long": "Oculto de los resultados de búsqueda, tendencias y líneas temporales públicas de Mastodon",
|
"privacy.unlisted.long": "Oculto de los resultados de búsqueda, tendencias y líneas temporales públicas de Mastodon",
|
||||||
"privacy.unlisted.short": "Público silencioso",
|
"privacy.unlisted.short": "Público pero silencioso",
|
||||||
"privacy_policy.last_updated": "Última actualización: {date}",
|
"privacy_policy.last_updated": "Última actualización: {date}",
|
||||||
"privacy_policy.title": "Política de privacidad",
|
"privacy_policy.title": "Política de privacidad",
|
||||||
"quote_error.edit": "Las citas no se pueden agregar al editar un mensaje.",
|
"quote_error.edit": "Las citas no se pueden agregar al editar un mensaje.",
|
||||||
@ -935,7 +935,7 @@
|
|||||||
"status.quote_policy_change": "Cambiá quién puede citar",
|
"status.quote_policy_change": "Cambiá quién puede citar",
|
||||||
"status.quote_post_author": "Se citó un mensaje de @{name}",
|
"status.quote_post_author": "Se citó un mensaje de @{name}",
|
||||||
"status.quote_private": "No se pueden citar los mensajes privados",
|
"status.quote_private": "No se pueden citar los mensajes privados",
|
||||||
"status.quotes": "{count, plural, one {# voto} other {# votos}}",
|
"status.quotes": "{count, plural, one {voto} other {votos}}",
|
||||||
"status.quotes.empty": "Todavía nadie citó este mensaje. Cuando alguien lo haga, se mostrará acá.",
|
"status.quotes.empty": "Todavía nadie citó este mensaje. Cuando alguien lo haga, se mostrará acá.",
|
||||||
"status.quotes.local_other_disclaimer": "Las citas rechazadas por el autor no serán mostradas.",
|
"status.quotes.local_other_disclaimer": "Las citas rechazadas por el autor no serán mostradas.",
|
||||||
"status.quotes.remote_other_disclaimer": "Solo las citas de {domain} están garantizadas de ser mostradas acá. Las citas rechazadas por el autor no serán mostradas.",
|
"status.quotes.remote_other_disclaimer": "Solo las citas de {domain} están garantizadas de ser mostradas acá. Las citas rechazadas por el autor no serán mostradas.",
|
||||||
|
|||||||
@ -249,6 +249,11 @@
|
|||||||
"confirmations.missing_alt_text.secondary": "Publier quand-même",
|
"confirmations.missing_alt_text.secondary": "Publier quand-même",
|
||||||
"confirmations.missing_alt_text.title": "Ajouter un texte alternatif?",
|
"confirmations.missing_alt_text.title": "Ajouter un texte alternatif?",
|
||||||
"confirmations.mute.confirm": "Masquer",
|
"confirmations.mute.confirm": "Masquer",
|
||||||
|
"confirmations.private_quote_notify.cancel": "Retour à l'édition",
|
||||||
|
"confirmations.private_quote_notify.confirm": "Publier",
|
||||||
|
"confirmations.private_quote_notify.do_not_show_again": "Ne plus afficher ce message",
|
||||||
|
"confirmations.private_quote_notify.message": "La personne citée et celles mentionnées seront notifiées et pourront voir le message, même si elles ne vous suivent pas.",
|
||||||
|
"confirmations.private_quote_notify.title": "Partager avec les personnes abonnées et mentionnées ?",
|
||||||
"confirmations.quiet_post_quote_info.dismiss": "Ne plus me rappeler",
|
"confirmations.quiet_post_quote_info.dismiss": "Ne plus me rappeler",
|
||||||
"confirmations.quiet_post_quote_info.got_it": "Compris",
|
"confirmations.quiet_post_quote_info.got_it": "Compris",
|
||||||
"confirmations.quiet_post_quote_info.message": "Lorsque vous citez un message public silencieux, votre message sera caché des fils tendances.",
|
"confirmations.quiet_post_quote_info.message": "Lorsque vous citez un message public silencieux, votre message sera caché des fils tendances.",
|
||||||
@ -760,6 +765,7 @@
|
|||||||
"privacy_policy.title": "Politique de confidentialité",
|
"privacy_policy.title": "Politique de confidentialité",
|
||||||
"quote_error.edit": "Les citations ne peuvent pas être ajoutés lors de l'édition d'un message.",
|
"quote_error.edit": "Les citations ne peuvent pas être ajoutés lors de l'édition d'un message.",
|
||||||
"quote_error.poll": "Les citations ne sont pas autorisées avec les sondages.",
|
"quote_error.poll": "Les citations ne sont pas autorisées avec les sondages.",
|
||||||
|
"quote_error.private_mentions": "La citation n'est pas autorisée avec les mentions privées.",
|
||||||
"quote_error.quote": "Une seule citation à la fois est autorisée.",
|
"quote_error.quote": "Une seule citation à la fois est autorisée.",
|
||||||
"quote_error.unauthorized": "Vous n'êtes pas autorisé⋅e à citer cette publication.",
|
"quote_error.unauthorized": "Vous n'êtes pas autorisé⋅e à citer cette publication.",
|
||||||
"quote_error.upload": "La citation n'est pas autorisée avec un média joint.",
|
"quote_error.upload": "La citation n'est pas autorisée avec un média joint.",
|
||||||
@ -1013,6 +1019,8 @@
|
|||||||
"video.volume_down": "Baisser le volume",
|
"video.volume_down": "Baisser le volume",
|
||||||
"video.volume_up": "Augmenter le volume",
|
"video.volume_up": "Augmenter le volume",
|
||||||
"visibility_modal.button_title": "Définir la visibilité",
|
"visibility_modal.button_title": "Définir la visibilité",
|
||||||
|
"visibility_modal.direct_quote_warning.text": "Si vous enregistrez les paramètres actuels, la citation sera convertie en lien.",
|
||||||
|
"visibility_modal.direct_quote_warning.title": "Les citations ne peuvent pas être intégrées dans les mentions privées",
|
||||||
"visibility_modal.header": "Visibilité et interactions",
|
"visibility_modal.header": "Visibilité et interactions",
|
||||||
"visibility_modal.helper.direct_quoting": "Les mentions privées rédigées sur Mastodon ne peuvent pas être citées par d'autres personnes.",
|
"visibility_modal.helper.direct_quoting": "Les mentions privées rédigées sur Mastodon ne peuvent pas être citées par d'autres personnes.",
|
||||||
"visibility_modal.helper.privacy_editing": "La visibilité ne peut pas être modifiée après la publication d'un message.",
|
"visibility_modal.helper.privacy_editing": "La visibilité ne peut pas être modifiée après la publication d'un message.",
|
||||||
|
|||||||
@ -249,6 +249,11 @@
|
|||||||
"confirmations.missing_alt_text.secondary": "Publier quand-même",
|
"confirmations.missing_alt_text.secondary": "Publier quand-même",
|
||||||
"confirmations.missing_alt_text.title": "Ajouter un texte alternatif?",
|
"confirmations.missing_alt_text.title": "Ajouter un texte alternatif?",
|
||||||
"confirmations.mute.confirm": "Masquer",
|
"confirmations.mute.confirm": "Masquer",
|
||||||
|
"confirmations.private_quote_notify.cancel": "Retour à l'édition",
|
||||||
|
"confirmations.private_quote_notify.confirm": "Publier",
|
||||||
|
"confirmations.private_quote_notify.do_not_show_again": "Ne plus afficher ce message",
|
||||||
|
"confirmations.private_quote_notify.message": "La personne citée et celles mentionnées seront notifiées et pourront voir le message, même si elles ne vous suivent pas.",
|
||||||
|
"confirmations.private_quote_notify.title": "Partager avec les personnes abonnées et mentionnées ?",
|
||||||
"confirmations.quiet_post_quote_info.dismiss": "Ne plus me rappeler",
|
"confirmations.quiet_post_quote_info.dismiss": "Ne plus me rappeler",
|
||||||
"confirmations.quiet_post_quote_info.got_it": "Compris",
|
"confirmations.quiet_post_quote_info.got_it": "Compris",
|
||||||
"confirmations.quiet_post_quote_info.message": "Lorsque vous citez un message public silencieux, votre message sera caché des fils tendances.",
|
"confirmations.quiet_post_quote_info.message": "Lorsque vous citez un message public silencieux, votre message sera caché des fils tendances.",
|
||||||
@ -760,6 +765,7 @@
|
|||||||
"privacy_policy.title": "Politique de confidentialité",
|
"privacy_policy.title": "Politique de confidentialité",
|
||||||
"quote_error.edit": "Les citations ne peuvent pas être ajoutés lors de l'édition d'un message.",
|
"quote_error.edit": "Les citations ne peuvent pas être ajoutés lors de l'édition d'un message.",
|
||||||
"quote_error.poll": "Les citations ne sont pas autorisées avec les sondages.",
|
"quote_error.poll": "Les citations ne sont pas autorisées avec les sondages.",
|
||||||
|
"quote_error.private_mentions": "La citation n'est pas autorisée avec les mentions privées.",
|
||||||
"quote_error.quote": "Une seule citation à la fois est autorisée.",
|
"quote_error.quote": "Une seule citation à la fois est autorisée.",
|
||||||
"quote_error.unauthorized": "Vous n'êtes pas autorisé⋅e à citer cette publication.",
|
"quote_error.unauthorized": "Vous n'êtes pas autorisé⋅e à citer cette publication.",
|
||||||
"quote_error.upload": "La citation n'est pas autorisée avec un média joint.",
|
"quote_error.upload": "La citation n'est pas autorisée avec un média joint.",
|
||||||
@ -1013,6 +1019,8 @@
|
|||||||
"video.volume_down": "Baisser le volume",
|
"video.volume_down": "Baisser le volume",
|
||||||
"video.volume_up": "Augmenter le volume",
|
"video.volume_up": "Augmenter le volume",
|
||||||
"visibility_modal.button_title": "Définir la visibilité",
|
"visibility_modal.button_title": "Définir la visibilité",
|
||||||
|
"visibility_modal.direct_quote_warning.text": "Si vous enregistrez les paramètres actuels, la citation sera convertie en lien.",
|
||||||
|
"visibility_modal.direct_quote_warning.title": "Les citations ne peuvent pas être intégrées dans les mentions privées",
|
||||||
"visibility_modal.header": "Visibilité et interactions",
|
"visibility_modal.header": "Visibilité et interactions",
|
||||||
"visibility_modal.helper.direct_quoting": "Les mentions privées rédigées sur Mastodon ne peuvent pas être citées par d'autres personnes.",
|
"visibility_modal.helper.direct_quoting": "Les mentions privées rédigées sur Mastodon ne peuvent pas être citées par d'autres personnes.",
|
||||||
"visibility_modal.helper.privacy_editing": "La visibilité ne peut pas être modifiée après la publication d'un message.",
|
"visibility_modal.helper.privacy_editing": "La visibilité ne peut pas être modifiée après la publication d'un message.",
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"about.blocks": "मॉडरेट सर्वर",
|
"about.blocks": "मॉडरेट सर्वर",
|
||||||
"about.contact": "कांटेक्ट:",
|
"about.contact": "कांटेक्ट:",
|
||||||
|
"about.default_locale": "Default",
|
||||||
"about.disclaimer": "मास्टोडन एक ओपन सोर्स सॉफ्टवेयर है, और मास्टोडन gGmbH का ट्रेडमार्क है।",
|
"about.disclaimer": "मास्टोडन एक ओपन सोर्स सॉफ्टवेयर है, और मास्टोडन gGmbH का ट्रेडमार्क है।",
|
||||||
"about.domain_blocks.no_reason_available": "कारण उपलब्ध नहीं है!",
|
"about.domain_blocks.no_reason_available": "कारण उपलब्ध नहीं है!",
|
||||||
"about.domain_blocks.preamble": "मास्टोडन आम तौर पर आपको कंटेंट को देखने और फेडिवेर्से में किसी अन्य सर्वर से उपयोगकर्ताओं के साथ बातचीत करने की अनुमति देता है। ये अपवाद हैं जो इस विशेष सर्वर पर बनाए गए हैं।",
|
"about.domain_blocks.preamble": "मास्टोडन आम तौर पर आपको कंटेंट को देखने और फेडिवेर्से में किसी अन्य सर्वर से उपयोगकर्ताओं के साथ बातचीत करने की अनुमति देता है। ये अपवाद हैं जो इस विशेष सर्वर पर बनाए गए हैं।",
|
||||||
|
|||||||
@ -249,6 +249,11 @@
|
|||||||
"confirmations.missing_alt_text.secondary": "就按呢PO出去",
|
"confirmations.missing_alt_text.secondary": "就按呢PO出去",
|
||||||
"confirmations.missing_alt_text.title": "Kám beh加添說明文字?",
|
"confirmations.missing_alt_text.title": "Kám beh加添說明文字?",
|
||||||
"confirmations.mute.confirm": "消音",
|
"confirmations.mute.confirm": "消音",
|
||||||
|
"confirmations.private_quote_notify.cancel": "轉去編輯",
|
||||||
|
"confirmations.private_quote_notify.confirm": "發布PO文",
|
||||||
|
"confirmations.private_quote_notify.do_not_show_again": "Mài koh展示tsit ê訊息",
|
||||||
|
"confirmations.private_quote_notify.message": "Lí所引用kap提起ê ē受通知,而且ē當看lí êPO文,就算in無跟tuè汝。",
|
||||||
|
"confirmations.private_quote_notify.title": "Kám beh kap跟tuè lí ê hām所提起ê用者分享?",
|
||||||
"confirmations.quiet_post_quote_info.dismiss": "請mài koh提醒我",
|
"confirmations.quiet_post_quote_info.dismiss": "請mài koh提醒我",
|
||||||
"confirmations.quiet_post_quote_info.got_it": "知ah",
|
"confirmations.quiet_post_quote_info.got_it": "知ah",
|
||||||
"confirmations.quiet_post_quote_info.message": "Nā是引用無tī公共時間線內底êPO文,lí êPO文bē當tī趨勢ê時間線顯示。",
|
"confirmations.quiet_post_quote_info.message": "Nā是引用無tī公共時間線內底êPO文,lí êPO文bē當tī趨勢ê時間線顯示。",
|
||||||
@ -760,6 +765,7 @@
|
|||||||
"privacy_policy.title": "隱私權政策",
|
"privacy_policy.title": "隱私權政策",
|
||||||
"quote_error.edit": "佇編輯PO文ê時陣bē當加引文。",
|
"quote_error.edit": "佇編輯PO文ê時陣bē當加引文。",
|
||||||
"quote_error.poll": "有投票ê PO文bē當引用。",
|
"quote_error.poll": "有投票ê PO文bē當引用。",
|
||||||
|
"quote_error.private_mentions": "私訊bē當允准引用。",
|
||||||
"quote_error.quote": "Tsi̍t改kan-ta ē當引用tsi̍t篇PO文。",
|
"quote_error.quote": "Tsi̍t改kan-ta ē當引用tsi̍t篇PO文。",
|
||||||
"quote_error.unauthorized": "Lí bô權利引用tsit篇PO文。",
|
"quote_error.unauthorized": "Lí bô權利引用tsit篇PO文。",
|
||||||
"quote_error.upload": "有媒體附件ê PO文無允准引用。",
|
"quote_error.upload": "有媒體附件ê PO文無允准引用。",
|
||||||
|
|||||||
@ -765,6 +765,7 @@
|
|||||||
"privacy_policy.title": "Privacybeleid",
|
"privacy_policy.title": "Privacybeleid",
|
||||||
"quote_error.edit": "Citaten kunnen niet tijdens het bewerken van een bericht worden toegevoegd.",
|
"quote_error.edit": "Citaten kunnen niet tijdens het bewerken van een bericht worden toegevoegd.",
|
||||||
"quote_error.poll": "Het is niet mogelijk om polls te citeren.",
|
"quote_error.poll": "Het is niet mogelijk om polls te citeren.",
|
||||||
|
"quote_error.private_mentions": "Citaten zijn niet toegestaan met directe vermeldingen.",
|
||||||
"quote_error.quote": "Je kunt maar één bericht per keer citeren.",
|
"quote_error.quote": "Je kunt maar één bericht per keer citeren.",
|
||||||
"quote_error.unauthorized": "Je bent niet gemachtigd om dit bericht te citeren.",
|
"quote_error.unauthorized": "Je bent niet gemachtigd om dit bericht te citeren.",
|
||||||
"quote_error.upload": "Je kunt geen mediabijlage aan een bericht met een citaat toevoegen.",
|
"quote_error.upload": "Je kunt geen mediabijlage aan een bericht met een citaat toevoegen.",
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
"account.disable_notifications": "Не уведомлять о постах пользователя @{name}",
|
"account.disable_notifications": "Не уведомлять о постах пользователя @{name}",
|
||||||
"account.domain_blocking": "Домен заблокирован",
|
"account.domain_blocking": "Домен заблокирован",
|
||||||
"account.edit_profile": "Редактировать",
|
"account.edit_profile": "Редактировать",
|
||||||
|
"account.edit_profile_short": "Редактировать",
|
||||||
"account.enable_notifications": "Уведомлять о постах пользователя @{name}",
|
"account.enable_notifications": "Уведомлять о постах пользователя @{name}",
|
||||||
"account.endorse": "Рекомендовать в профиле",
|
"account.endorse": "Рекомендовать в профиле",
|
||||||
"account.familiar_followers_many": "В подписках у {name1}, {name2}, и ещё {othersCount, plural, one {# человека, которого вы знаете} other {# человек, которых вы знаете}}",
|
"account.familiar_followers_many": "В подписках у {name1}, {name2}, и ещё {othersCount, plural, one {# человека, которого вы знаете} other {# человек, которых вы знаете}}",
|
||||||
@ -40,6 +41,11 @@
|
|||||||
"account.featured_tags.last_status_never": "Нет постов",
|
"account.featured_tags.last_status_never": "Нет постов",
|
||||||
"account.follow": "Подписаться",
|
"account.follow": "Подписаться",
|
||||||
"account.follow_back": "Подписаться в ответ",
|
"account.follow_back": "Подписаться в ответ",
|
||||||
|
"account.follow_back_short": "Подписаться",
|
||||||
|
"account.follow_request": "Отправить запрос на подписку",
|
||||||
|
"account.follow_request_cancel": "Отозвать запрос",
|
||||||
|
"account.follow_request_cancel_short": "Отозвать",
|
||||||
|
"account.follow_request_short": "Отправить запрос",
|
||||||
"account.followers": "Подписчики",
|
"account.followers": "Подписчики",
|
||||||
"account.followers.empty": "На этого пользователя пока никто не подписан.",
|
"account.followers.empty": "На этого пользователя пока никто не подписан.",
|
||||||
"account.followers_counter": "{count, plural, one {{counter} подписчик} few {{counter} подписчика} other {{counter} подписчиков}}",
|
"account.followers_counter": "{count, plural, one {{counter} подписчик} few {{counter} подписчика} other {{counter} подписчиков}}",
|
||||||
@ -167,6 +173,8 @@
|
|||||||
"column.edit_list": "Редактировать список",
|
"column.edit_list": "Редактировать список",
|
||||||
"column.favourites": "Избранное",
|
"column.favourites": "Избранное",
|
||||||
"column.firehose": "Живая лента",
|
"column.firehose": "Живая лента",
|
||||||
|
"column.firehose_local": "Живая лента этого сервера",
|
||||||
|
"column.firehose_singular": "Живая лента",
|
||||||
"column.follow_requests": "Запросы на подписку",
|
"column.follow_requests": "Запросы на подписку",
|
||||||
"column.home": "Главная",
|
"column.home": "Главная",
|
||||||
"column.list_members": "Пользователи в списке",
|
"column.list_members": "Пользователи в списке",
|
||||||
@ -186,6 +194,7 @@
|
|||||||
"community.column_settings.local_only": "Только локальные",
|
"community.column_settings.local_only": "Только локальные",
|
||||||
"community.column_settings.media_only": "Только с медиафайлами",
|
"community.column_settings.media_only": "Только с медиафайлами",
|
||||||
"community.column_settings.remote_only": "Только с других серверов",
|
"community.column_settings.remote_only": "Только с других серверов",
|
||||||
|
"compose.error.blank_post": "Нельзя опубликовать пустой пост.",
|
||||||
"compose.language.change": "Изменить язык",
|
"compose.language.change": "Изменить язык",
|
||||||
"compose.language.search": "Найти язык...",
|
"compose.language.search": "Найти язык...",
|
||||||
"compose.published.body": "Пост опубликован.",
|
"compose.published.body": "Пост опубликован.",
|
||||||
@ -238,20 +247,30 @@
|
|||||||
"confirmations.missing_alt_text.secondary": "Опубликовать",
|
"confirmations.missing_alt_text.secondary": "Опубликовать",
|
||||||
"confirmations.missing_alt_text.title": "Добавить альтернативный текст?",
|
"confirmations.missing_alt_text.title": "Добавить альтернативный текст?",
|
||||||
"confirmations.mute.confirm": "Игнорировать",
|
"confirmations.mute.confirm": "Игнорировать",
|
||||||
"confirmations.quiet_post_quote_info.dismiss": "Больше не показывать",
|
"confirmations.private_quote_notify.cancel": "Вернуться к редактированию",
|
||||||
|
"confirmations.private_quote_notify.confirm": "Опубликовать",
|
||||||
|
"confirmations.private_quote_notify.do_not_show_again": "Не показывать это сообщение снова",
|
||||||
|
"confirmations.private_quote_notify.message": "Пользователь, которого вы процитировали, а также другие упомянутые пользователи будут уведомлены и смогут просмотреть ваш пост, даже если они не подписаны на вас.",
|
||||||
|
"confirmations.private_quote_notify.title": "Поделиться с подписчиками и упомянутыми пользователями?",
|
||||||
|
"confirmations.quiet_post_quote_info.dismiss": "Больше не напоминать",
|
||||||
"confirmations.quiet_post_quote_info.got_it": "Понятно",
|
"confirmations.quiet_post_quote_info.got_it": "Понятно",
|
||||||
"confirmations.quiet_post_quote_info.message": "Если вы цитируете публикацию, опубликованную в открытом доступе, ваша публикация будет скрыта от новостных лент.",
|
"confirmations.quiet_post_quote_info.message": "Если ваш пост содержит цитирование «тихого публичного» поста, он будет скрыт из алгоритмических лент.",
|
||||||
"confirmations.quiet_post_quote_info.title": "Цитирование тихих публичных постов",
|
"confirmations.quiet_post_quote_info.title": "Цитирование «тихих публичных» постов",
|
||||||
"confirmations.redraft.confirm": "Удалить и исправить",
|
"confirmations.redraft.confirm": "Удалить и исправить",
|
||||||
"confirmations.redraft.message": "Вы уверены, что хотите удалить этот пост и создать его заново? Взаимодействия, такие как добавление в избранное и продвижение, будут потеряны, а ответы к оригинальному посту перестанут на него ссылаться.",
|
"confirmations.redraft.message": "Вы уверены, что хотите удалить этот пост и создать его заново? Взаимодействия, такие как добавление в избранное и продвижение, будут потеряны, а ответы к оригинальному посту перестанут на него ссылаться.",
|
||||||
"confirmations.redraft.title": "Удалить и создать пост заново?",
|
"confirmations.redraft.title": "Удалить и создать пост заново?",
|
||||||
"confirmations.remove_from_followers.confirm": "Убрать подписчика",
|
"confirmations.remove_from_followers.confirm": "Убрать подписчика",
|
||||||
"confirmations.remove_from_followers.message": "Пользователь {name} перестанет быть подписан на вас. Продолжить?",
|
"confirmations.remove_from_followers.message": "Пользователь {name} перестанет быть подписан на вас. Продолжить?",
|
||||||
"confirmations.remove_from_followers.title": "Убрать подписчика?",
|
"confirmations.remove_from_followers.title": "Убрать подписчика?",
|
||||||
"confirmations.revoke_quote.confirm": "Удалить публикацию",
|
"confirmations.revoke_quote.confirm": "Убрать пост",
|
||||||
"confirmations.revoke_quote.message": "Это действие нельзя будет отменить.",
|
"confirmations.revoke_quote.message": "Это действие невозможно отменить.",
|
||||||
"confirmations.revoke_quote.title": "Удалить публикацию?",
|
"confirmations.revoke_quote.title": "Убрать пост?",
|
||||||
|
"confirmations.unblock.confirm": "Разблокировать",
|
||||||
|
"confirmations.unblock.title": "Разблокировать {name}?",
|
||||||
"confirmations.unfollow.confirm": "Отписаться",
|
"confirmations.unfollow.confirm": "Отписаться",
|
||||||
|
"confirmations.unfollow.title": "Отписаться от {name}?",
|
||||||
|
"confirmations.withdraw_request.confirm": "Отозвать запрос",
|
||||||
|
"confirmations.withdraw_request.title": "Отозвать запрос на подписку на {name}?",
|
||||||
"content_warning.hide": "Скрыть пост",
|
"content_warning.hide": "Скрыть пост",
|
||||||
"content_warning.show": "Всё равно показать",
|
"content_warning.show": "Всё равно показать",
|
||||||
"content_warning.show_more": "Развернуть",
|
"content_warning.show_more": "Развернуть",
|
||||||
@ -322,6 +341,7 @@
|
|||||||
"empty_column.bookmarked_statuses": "У вас пока нет закладок. Когда вы добавляете пост в закладки, он появляется здесь.",
|
"empty_column.bookmarked_statuses": "У вас пока нет закладок. Когда вы добавляете пост в закладки, он появляется здесь.",
|
||||||
"empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!",
|
"empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!",
|
||||||
"empty_column.direct": "Вы ещё не упоминали кого-либо и сами не были ни разу упомянуты лично. Все личные упоминания будут показаны здесь.",
|
"empty_column.direct": "Вы ещё не упоминали кого-либо и сами не были ни разу упомянуты лично. Все личные упоминания будут показаны здесь.",
|
||||||
|
"empty_column.disabled_feed": "Эта лента была отключена администраторами вашего сервера.",
|
||||||
"empty_column.domain_blocks": "Заблокированных доменов пока нет.",
|
"empty_column.domain_blocks": "Заблокированных доменов пока нет.",
|
||||||
"empty_column.explore_statuses": "Сейчас нет популярных постов. Проверьте позже!",
|
"empty_column.explore_statuses": "Сейчас нет популярных постов. Проверьте позже!",
|
||||||
"empty_column.favourited_statuses": "Вы ещё не добавили ни одного поста в избранное. Все добавленные вами в избранное посты будут показаны здесь.",
|
"empty_column.favourited_statuses": "Вы ещё не добавили ни одного поста в избранное. Все добавленные вами в избранное посты будут показаны здесь.",
|
||||||
@ -346,7 +366,9 @@
|
|||||||
"explore.trending_links": "Новости",
|
"explore.trending_links": "Новости",
|
||||||
"explore.trending_statuses": "Посты",
|
"explore.trending_statuses": "Посты",
|
||||||
"explore.trending_tags": "Хештеги",
|
"explore.trending_tags": "Хештеги",
|
||||||
|
"featured_carousel.current": "<sr>Пост</sr> {current, number} / {max, number}",
|
||||||
"featured_carousel.header": "{count, plural, other {Закреплённые посты}}",
|
"featured_carousel.header": "{count, plural, other {Закреплённые посты}}",
|
||||||
|
"featured_carousel.slide": "Пост {current, number} из {max, number}",
|
||||||
"filter_modal.added.context_mismatch_explanation": "Этот фильтр не применяется в том контексте, в котором вы видели этот пост. Если вы хотите, чтобы пост был отфильтрован в текущем контексте, необходимо редактировать фильтр.",
|
"filter_modal.added.context_mismatch_explanation": "Этот фильтр не применяется в том контексте, в котором вы видели этот пост. Если вы хотите, чтобы пост был отфильтрован в текущем контексте, необходимо редактировать фильтр.",
|
||||||
"filter_modal.added.context_mismatch_title": "Несоответствие контекста",
|
"filter_modal.added.context_mismatch_title": "Несоответствие контекста",
|
||||||
"filter_modal.added.expired_explanation": "Этот фильтр истёк. Чтобы он был применён, вам нужно изменить срок действия фильтра.",
|
"filter_modal.added.expired_explanation": "Этот фильтр истёк. Чтобы он был применён, вам нужно изменить срок действия фильтра.",
|
||||||
@ -389,6 +411,7 @@
|
|||||||
"follow_suggestions.who_to_follow": "На кого подписаться",
|
"follow_suggestions.who_to_follow": "На кого подписаться",
|
||||||
"followed_tags": "Подписки на хештеги",
|
"followed_tags": "Подписки на хештеги",
|
||||||
"footer.about": "О проекте",
|
"footer.about": "О проекте",
|
||||||
|
"footer.about_this_server": "Об этом сервере",
|
||||||
"footer.directory": "Каталог профилей",
|
"footer.directory": "Каталог профилей",
|
||||||
"footer.get_app": "Скачать приложение",
|
"footer.get_app": "Скачать приложение",
|
||||||
"footer.keyboard_shortcuts": "Сочетания клавиш",
|
"footer.keyboard_shortcuts": "Сочетания клавиш",
|
||||||
@ -472,7 +495,7 @@
|
|||||||
"keyboard_shortcuts.home": "перейти к домашней ленте",
|
"keyboard_shortcuts.home": "перейти к домашней ленте",
|
||||||
"keyboard_shortcuts.hotkey": "Горячая клавиша",
|
"keyboard_shortcuts.hotkey": "Горячая клавиша",
|
||||||
"keyboard_shortcuts.legend": "показать эту справку",
|
"keyboard_shortcuts.legend": "показать эту справку",
|
||||||
"keyboard_shortcuts.load_more": "Акцент на кнопке «Загрузить ещё»",
|
"keyboard_shortcuts.load_more": "фокус на кнопке «Загрузить ещё»",
|
||||||
"keyboard_shortcuts.local": "перейти к локальной ленте",
|
"keyboard_shortcuts.local": "перейти к локальной ленте",
|
||||||
"keyboard_shortcuts.mention": "упомянуть автора поста",
|
"keyboard_shortcuts.mention": "упомянуть автора поста",
|
||||||
"keyboard_shortcuts.muted": "открыть список игнорируемых пользователей",
|
"keyboard_shortcuts.muted": "открыть список игнорируемых пользователей",
|
||||||
@ -481,7 +504,7 @@
|
|||||||
"keyboard_shortcuts.open_media": "открыть медиа",
|
"keyboard_shortcuts.open_media": "открыть медиа",
|
||||||
"keyboard_shortcuts.pinned": "перейти к закреплённым постам",
|
"keyboard_shortcuts.pinned": "перейти к закреплённым постам",
|
||||||
"keyboard_shortcuts.profile": "перейти к профилю автора",
|
"keyboard_shortcuts.profile": "перейти к профилю автора",
|
||||||
"keyboard_shortcuts.quote": "Цитировать пост",
|
"keyboard_shortcuts.quote": "цитировать пост",
|
||||||
"keyboard_shortcuts.reply": "ответить",
|
"keyboard_shortcuts.reply": "ответить",
|
||||||
"keyboard_shortcuts.requests": "перейти к запросам на подписку",
|
"keyboard_shortcuts.requests": "перейти к запросам на подписку",
|
||||||
"keyboard_shortcuts.search": "перейти к поиску",
|
"keyboard_shortcuts.search": "перейти к поиску",
|
||||||
@ -730,14 +753,15 @@
|
|||||||
"privacy.private.short": "Для подписчиков",
|
"privacy.private.short": "Для подписчиков",
|
||||||
"privacy.public.long": "Для кого угодно в интернете",
|
"privacy.public.long": "Для кого угодно в интернете",
|
||||||
"privacy.public.short": "Публичный",
|
"privacy.public.short": "Публичный",
|
||||||
"privacy.quote.anyone": "{visibility}, каждый может процитировать",
|
"privacy.quote.anyone": "{visibility}, цитировать разрешено всем",
|
||||||
"privacy.quote.disabled": "{visibility}, цитаты отключены",
|
"privacy.quote.disabled": "{visibility}, без возможности цитирования",
|
||||||
"privacy.quote.limited": "{visibility}, цитаты ограничены",
|
"privacy.quote.limited": "{visibility}, с ограничениями цитирования",
|
||||||
"privacy.unlisted.additional": "Похоже на «Публичный» за исключением того, что пост не появится ни в живых лентах, ни в лентах хештегов, ни в разделе «Актуальное», ни в поиске Mastodon, даже если вы разрешили поиск по своим постам в настройках профиля.",
|
"privacy.unlisted.additional": "Похоже на «Публичный» за исключением того, что пост не появится ни в живых лентах, ни в лентах хештегов, ни в разделе «Актуальное», ни в поиске Mastodon, даже если вы разрешили поиск по своим постам в настройках профиля.",
|
||||||
"privacy.unlisted.long": "Скрыто от результатов поиска Mastodon, трендов и публичных графиков",
|
"privacy.unlisted.long": "Не показывать в результатах поиска Mastodon, трендах и публичных лентах",
|
||||||
"privacy.unlisted.short": "Тихий публичный",
|
"privacy.unlisted.short": "Тихий публичный",
|
||||||
"privacy_policy.last_updated": "Последнее обновление: {date}",
|
"privacy_policy.last_updated": "Последнее обновление: {date}",
|
||||||
"privacy_policy.title": "Политика конфиденциальности",
|
"privacy_policy.title": "Политика конфиденциальности",
|
||||||
|
"quote_error.edit": "Нельзя добавить цитирование к уже опубликованному посту.",
|
||||||
"quote_error.poll": "Цитирование не допускается при голосовании.",
|
"quote_error.poll": "Цитирование не допускается при голосовании.",
|
||||||
"quote_error.quote": "Одновременно допускается только одна цитата.",
|
"quote_error.quote": "Одновременно допускается только одна цитата.",
|
||||||
"quote_error.unauthorized": "Вы не имеете права цитировать этот пост.",
|
"quote_error.unauthorized": "Вы не имеете права цитировать этот пост.",
|
||||||
@ -759,7 +783,6 @@
|
|||||||
"relative_time.today": "сегодня",
|
"relative_time.today": "сегодня",
|
||||||
"remove_quote_hint.button_label": "Понятно",
|
"remove_quote_hint.button_label": "Понятно",
|
||||||
"remove_quote_hint.message": "Вы можете сделать это из меню настроек {icon}.",
|
"remove_quote_hint.message": "Вы можете сделать это из меню настроек {icon}.",
|
||||||
"remove_quote_hint.title": "Хотите удалить цитируемый вами пост?",
|
|
||||||
"reply_indicator.attachments": "{count, plural, one {# вложение} few {# вложения} other {# вложений}}",
|
"reply_indicator.attachments": "{count, plural, one {# вложение} few {# вложения} other {# вложений}}",
|
||||||
"reply_indicator.cancel": "Отмена",
|
"reply_indicator.cancel": "Отмена",
|
||||||
"reply_indicator.poll": "Опрос",
|
"reply_indicator.poll": "Опрос",
|
||||||
@ -851,15 +874,22 @@
|
|||||||
"status.admin_account": "Открыть интерфейс модератора для @{name}",
|
"status.admin_account": "Открыть интерфейс модератора для @{name}",
|
||||||
"status.admin_domain": "Открыть интерфейс модератора для {domain}",
|
"status.admin_domain": "Открыть интерфейс модератора для {domain}",
|
||||||
"status.admin_status": "Открыть этот пост в интерфейсе модератора",
|
"status.admin_status": "Открыть этот пост в интерфейсе модератора",
|
||||||
"status.all_disabled": "Бусты и цитаты отключены",
|
"status.all_disabled": "Нельзя продвинуть или процитировать",
|
||||||
"status.block": "Заблокировать @{name}",
|
"status.block": "Заблокировать @{name}",
|
||||||
"status.bookmark": "Добавить в закладки",
|
"status.bookmark": "Добавить в закладки",
|
||||||
"status.cancel_reblog_private": "Отменить продвижение",
|
"status.cancel_reblog_private": "Отменить продвижение",
|
||||||
|
"status.cannot_quote": "Вы не можете процитировать этот пост",
|
||||||
"status.cannot_reblog": "Этот пост не может быть продвинут",
|
"status.cannot_reblog": "Этот пост не может быть продвинут",
|
||||||
|
"status.context.loading": "Загрузка ответов",
|
||||||
|
"status.context.loading_error": "Не удалось загрузить новые ответы",
|
||||||
|
"status.context.loading_success": "Загружены новые ответы",
|
||||||
|
"status.context.more_replies_found": "Найдены другие ответы",
|
||||||
|
"status.context.retry": "Повторить",
|
||||||
|
"status.context.show": "Показать",
|
||||||
"status.continued_thread": "Продолжение предыдущего поста",
|
"status.continued_thread": "Продолжение предыдущего поста",
|
||||||
"status.copy": "Скопировать ссылку на пост",
|
"status.copy": "Скопировать ссылку на пост",
|
||||||
"status.delete": "Удалить",
|
"status.delete": "Удалить",
|
||||||
"status.delete.success": "Пост удален",
|
"status.delete.success": "Пост удалён",
|
||||||
"status.detailed_status": "Подробный просмотр обсуждения",
|
"status.detailed_status": "Подробный просмотр обсуждения",
|
||||||
"status.direct": "Упомянуть @{name} лично",
|
"status.direct": "Упомянуть @{name} лично",
|
||||||
"status.direct_indicator": "Личное упоминание",
|
"status.direct_indicator": "Личное упоминание",
|
||||||
@ -884,22 +914,28 @@
|
|||||||
"status.pin": "Закрепить в профиле",
|
"status.pin": "Закрепить в профиле",
|
||||||
"status.quote": "Цитировать",
|
"status.quote": "Цитировать",
|
||||||
"status.quote.cancel": "Отменить цитирование",
|
"status.quote.cancel": "Отменить цитирование",
|
||||||
|
"status.quote_error.blocked_account_hint.title": "Этот пост был скрыт, поскольку вы заблокировали @{name}.",
|
||||||
|
"status.quote_error.blocked_domain_hint.title": "Этот пост был скрыт, поскольку вы заблокировали сервер {domain}.",
|
||||||
"status.quote_error.filtered": "Скрыто одним из ваших фильтров",
|
"status.quote_error.filtered": "Скрыто одним из ваших фильтров",
|
||||||
|
"status.quote_error.limited_account_hint.action": "Всё равно показать",
|
||||||
|
"status.quote_error.limited_account_hint.title": "Этот профиль был скрыт модераторами сервера {domain}.",
|
||||||
|
"status.quote_error.muted_account_hint.title": "Этот пост был скрыт, поскольку вы игнорируете @{name}.",
|
||||||
"status.quote_error.not_available": "Пост недоступен",
|
"status.quote_error.not_available": "Пост недоступен",
|
||||||
"status.quote_error.pending_approval": "Пост ожидает подтверждения",
|
"status.quote_error.pending_approval": "Пост ожидает подтверждения",
|
||||||
"status.quote_error.pending_approval_popout.body": "На Mastodon вы можете контролировать, будет ли кто-то цитировать вас. Этот пост находится на рассмотрении, пока мы не получим одобрение автора.",
|
"status.quote_error.pending_approval_popout.body": "На Mastodon вы можете контролировать, будет ли кто-то цитировать вас. Этот пост находится на рассмотрении, пока мы не получим одобрение автора.",
|
||||||
"status.quote_error.revoked": "Сообщение удалено автором",
|
"status.quote_error.revoked": "Пост удалён автором",
|
||||||
"status.quote_followers_only": "Только подписчики могут цитировать этот пост",
|
"status.quote_followers_only": "Только подписчики могут цитировать этот пост",
|
||||||
"status.quote_manual_review": "Автор будет проверять вручную",
|
"status.quote_manual_review": "Автор будет проверять вручную",
|
||||||
"status.quote_policy_change": "Измените, кто может цитировать",
|
"status.quote_noun": "Цитата",
|
||||||
"status.quote_post_author": "Пост цитировал @{name}",
|
"status.quote_policy_change": "Изменить настройки цитирования",
|
||||||
|
"status.quote_post_author": "Процитировал(а) пост @{name}",
|
||||||
"status.quote_private": "Приватные записи не могут быть цитированы",
|
"status.quote_private": "Приватные записи не могут быть цитированы",
|
||||||
"status.quotes": "{count, plural, one {# голос} few {# голоса} many {# голосов} other {# голосов}}",
|
"status.quotes": "{count, plural, one {цитирование} few {цитирования} other {цитирований}}",
|
||||||
"status.quotes.empty": "Никто еще не цитировал этот пост. Когда кто-нибудь это сделает, он появится здесь.",
|
"status.quotes.empty": "Никто еще не процитировал этот пост. Все цитирования этого поста будут показаны здесь.",
|
||||||
"status.read_more": "Читать далее",
|
"status.read_more": "Читать далее",
|
||||||
"status.reblog": "Продвинуть",
|
"status.reblog": "Продвинуть",
|
||||||
"status.reblog_or_quote": "Буст или цитата",
|
"status.reblog_or_quote": "Продвинуть или процитировать",
|
||||||
"status.reblog_private": "Поделиться снова со своими подписчиками",
|
"status.reblog_private": "Поделиться со своими подписчиками ещё раз",
|
||||||
"status.reblogged_by": "{name} продвинул(а)",
|
"status.reblogged_by": "{name} продвинул(а)",
|
||||||
"status.reblogs": "{count, plural, one {продвижение} few {продвижения} other {продвижений}}",
|
"status.reblogs": "{count, plural, one {продвижение} few {продвижения} other {продвижений}}",
|
||||||
"status.reblogs.empty": "Никто ещё не продвинул этот пост. Все пользователи, которые продвинут этот пост, будут показаны здесь.",
|
"status.reblogs.empty": "Никто ещё не продвинул этот пост. Все пользователи, которые продвинут этот пост, будут показаны здесь.",
|
||||||
@ -913,7 +949,7 @@
|
|||||||
"status.replyAll": "Ответить в обсуждении",
|
"status.replyAll": "Ответить в обсуждении",
|
||||||
"status.report": "Пожаловаться на @{name}",
|
"status.report": "Пожаловаться на @{name}",
|
||||||
"status.request_quote": "Запрос на цитирование",
|
"status.request_quote": "Запрос на цитирование",
|
||||||
"status.revoke_quote": "Удалить мой пост из поста @{name}",
|
"status.revoke_quote": "Убрать мой пост из поста @{name}",
|
||||||
"status.sensitive_warning": "Медиа деликатного характера",
|
"status.sensitive_warning": "Медиа деликатного характера",
|
||||||
"status.share": "Поделиться",
|
"status.share": "Поделиться",
|
||||||
"status.show_less_all": "Свернуть все предупреждения о содержании в ветке",
|
"status.show_less_all": "Свернуть все предупреждения о содержании в ветке",
|
||||||
@ -951,7 +987,7 @@
|
|||||||
"upload_button.label": "Прикрепить фото, видео или аудио",
|
"upload_button.label": "Прикрепить фото, видео или аудио",
|
||||||
"upload_error.limit": "Превышено максимальное количество вложений.",
|
"upload_error.limit": "Превышено максимальное количество вложений.",
|
||||||
"upload_error.poll": "К опросам нельзя прикреплять файлы.",
|
"upload_error.poll": "К опросам нельзя прикреплять файлы.",
|
||||||
"upload_error.quote": "К опросам нельзя прикреплять файлы.",
|
"upload_error.quote": "К цитированиям нельзя прикреплять файлы.",
|
||||||
"upload_form.drag_and_drop.instructions": "Чтобы выбрать вложение, нажмите \"Пробел\" (Space) или \"Ввод\" (Enter). Используйте клавиши со стрелками, чтобы передвинуть вложение в любом направлении. Нажмите \"Пробел\" (Space) или \"Ввод\" (Enter) ещё раз, чтобы переместить вложение на новое место, либо нажмите кнопку \"Выйти\" (Escape) для отмены перемещения.",
|
"upload_form.drag_and_drop.instructions": "Чтобы выбрать вложение, нажмите \"Пробел\" (Space) или \"Ввод\" (Enter). Используйте клавиши со стрелками, чтобы передвинуть вложение в любом направлении. Нажмите \"Пробел\" (Space) или \"Ввод\" (Enter) ещё раз, чтобы переместить вложение на новое место, либо нажмите кнопку \"Выйти\" (Escape) для отмены перемещения.",
|
||||||
"upload_form.drag_and_drop.on_drag_cancel": "Перемещение отменено. Вложение {item} было оставлено на прежнем месте.",
|
"upload_form.drag_and_drop.on_drag_cancel": "Перемещение отменено. Вложение {item} было оставлено на прежнем месте.",
|
||||||
"upload_form.drag_and_drop.on_drag_end": "Вложение {item} было перемещено.",
|
"upload_form.drag_and_drop.on_drag_end": "Вложение {item} было перемещено.",
|
||||||
@ -976,12 +1012,19 @@
|
|||||||
"video.volume_down": "Громкость уменьшена",
|
"video.volume_down": "Громкость уменьшена",
|
||||||
"video.volume_up": "Громкость увеличена",
|
"video.volume_up": "Громкость увеличена",
|
||||||
"visibility_modal.button_title": "Настроить видимость",
|
"visibility_modal.button_title": "Настроить видимость",
|
||||||
|
"visibility_modal.direct_quote_warning.text": "Если вы сохраните эти настройки, вложенное цитирование будет преобразовано в ссылку.",
|
||||||
|
"visibility_modal.direct_quote_warning.title": "Нельзя добавить цитирование к личным упоминаниям",
|
||||||
"visibility_modal.header": "Видимость и взаимодействие",
|
"visibility_modal.header": "Видимость и взаимодействие",
|
||||||
"visibility_modal.helper.direct_quoting": "Частные упоминания, созданные на Mastodon не могут быть цитированы другими.",
|
"visibility_modal.helper.direct_quoting": "Частные упоминания, созданные на Mastodon не могут быть цитированы другими.",
|
||||||
"visibility_modal.helper.privacy_editing": "После публикации поста его видимость нельзя изменить.",
|
"visibility_modal.helper.privacy_editing": "Видимость поста невозможно изменить после публикации.",
|
||||||
"visibility_modal.helper.privacy_private_self_quote": "Цитаты из личных сообщений не могут быть опубликованы публично.",
|
"visibility_modal.helper.privacy_private_self_quote": "Цитаты из личных сообщений не могут быть опубликованы публично.",
|
||||||
"visibility_modal.helper.private_quoting": "Публикации на Mastodon, доступные исключительно подписчикам, не подлежат цитированию со стороны других пользователей.",
|
"visibility_modal.helper.private_quoting": "Публикации на Mastodon, доступные исключительно подписчикам, не подлежат цитированию со стороны других пользователей.",
|
||||||
"visibility_modal.helper.unlisted_quoting": "Когда люди цитируют вас, их посты также скрываются из ленты трендов.",
|
"visibility_modal.helper.unlisted_quoting": "Когда люди цитируют вас, их посты также скрываются из ленты трендов.",
|
||||||
"visibility_modal.instructions": "Регулируйте, кто сможет взаимодействовать с этим сообщением. Также можно установить параметры для всех будущих публикаций, перейдя в <link>Настройки > Стандартные параметры публикации</link>.",
|
"visibility_modal.instructions": "Регулируйте, кто сможет взаимодействовать с этим сообщением. Также можно установить параметры для всех будущих публикаций, перейдя в <link>Настройки > Стандартные параметры публикации</link>.",
|
||||||
"visibility_modal.privacy_label": "Видимость"
|
"visibility_modal.privacy_label": "Видимость",
|
||||||
|
"visibility_modal.quote_followers": "Только подписчики",
|
||||||
|
"visibility_modal.quote_label": "Кто может цитировать вас",
|
||||||
|
"visibility_modal.quote_nobody": "Только я",
|
||||||
|
"visibility_modal.quote_public": "Кто угодно",
|
||||||
|
"visibility_modal.save": "Сохранить"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -192,6 +192,7 @@
|
|||||||
"community.column_settings.local_only": "Iba miestne",
|
"community.column_settings.local_only": "Iba miestne",
|
||||||
"community.column_settings.media_only": "Iba médiá",
|
"community.column_settings.media_only": "Iba médiá",
|
||||||
"community.column_settings.remote_only": "Iba vzdialené",
|
"community.column_settings.remote_only": "Iba vzdialené",
|
||||||
|
"compose.error.blank_post": "Príspevok nemôže byť prázdny.",
|
||||||
"compose.language.change": "Zmeniť jazyk",
|
"compose.language.change": "Zmeniť jazyk",
|
||||||
"compose.language.search": "Vyhľadávať jazyky…",
|
"compose.language.search": "Vyhľadávať jazyky…",
|
||||||
"compose.published.body": "Príspevok zverejnený.",
|
"compose.published.body": "Príspevok zverejnený.",
|
||||||
@ -226,6 +227,7 @@
|
|||||||
"confirmations.delete_list.title": "Vymazať zoznam?",
|
"confirmations.delete_list.title": "Vymazať zoznam?",
|
||||||
"confirmations.discard_draft.confirm": "Zahodiť a pokračovať",
|
"confirmations.discard_draft.confirm": "Zahodiť a pokračovať",
|
||||||
"confirmations.discard_draft.edit.cancel": "Pokračovať v úpravách",
|
"confirmations.discard_draft.edit.cancel": "Pokračovať v úpravách",
|
||||||
|
"confirmations.discard_draft.post.cancel": "Pokračuj v rozpísanom",
|
||||||
"confirmations.discard_edit_media.confirm": "Zahodiť",
|
"confirmations.discard_edit_media.confirm": "Zahodiť",
|
||||||
"confirmations.discard_edit_media.message": "Máte neuložené zmeny v popise alebo náhľade média, zahodiť ich aj tak?",
|
"confirmations.discard_edit_media.message": "Máte neuložené zmeny v popise alebo náhľade média, zahodiť ich aj tak?",
|
||||||
"confirmations.follow_to_list.confirm": "Nasleduj a pridaj do zoznamu",
|
"confirmations.follow_to_list.confirm": "Nasleduj a pridaj do zoznamu",
|
||||||
@ -239,14 +241,21 @@
|
|||||||
"confirmations.missing_alt_text.secondary": "Aj tak uverejniť",
|
"confirmations.missing_alt_text.secondary": "Aj tak uverejniť",
|
||||||
"confirmations.missing_alt_text.title": "Pridať opis?",
|
"confirmations.missing_alt_text.title": "Pridať opis?",
|
||||||
"confirmations.mute.confirm": "Stíšiť",
|
"confirmations.mute.confirm": "Stíšiť",
|
||||||
|
"confirmations.private_quote_notify.cancel": "Späť na úpravu",
|
||||||
|
"confirmations.private_quote_notify.confirm": "Zverejni príspevok",
|
||||||
|
"confirmations.private_quote_notify.do_not_show_again": "Neukazuj mi túto spravu znova",
|
||||||
|
"confirmations.quiet_post_quote_info.dismiss": "Nepripomínaj mi znova",
|
||||||
"confirmations.redraft.confirm": "Vymazať a prepísať",
|
"confirmations.redraft.confirm": "Vymazať a prepísať",
|
||||||
"confirmations.redraft.message": "Určite chcete tento príspevok vymazať a prepísať? Prídete o jeho zdieľania a ohviezdičkovania a odpovede na pôvodný príspevok budú odlúčené.",
|
"confirmations.redraft.message": "Určite chcete tento príspevok vymazať a prepísať? Prídete o jeho zdieľania a ohviezdičkovania a odpovede na pôvodný príspevok budú odlúčené.",
|
||||||
"confirmations.redraft.title": "Vymazať a prepísať príspevok?",
|
"confirmations.redraft.title": "Vymazať a prepísať príspevok?",
|
||||||
"confirmations.remove_from_followers.confirm": "Odstrániť nasledovateľa",
|
"confirmations.remove_from_followers.confirm": "Odstrániť nasledovateľa",
|
||||||
|
"confirmations.revoke_quote.confirm": "Vymazať príspevok",
|
||||||
|
"confirmations.revoke_quote.message": "Tento úkon sa nedá navrátiť.",
|
||||||
"confirmations.revoke_quote.title": "Vymazať príspevok?",
|
"confirmations.revoke_quote.title": "Vymazať príspevok?",
|
||||||
"confirmations.unblock.confirm": "Odblokovať",
|
"confirmations.unblock.confirm": "Odblokovať",
|
||||||
"confirmations.unblock.title": "Odblokovať {name}?",
|
"confirmations.unblock.title": "Odblokovať {name}?",
|
||||||
"confirmations.unfollow.confirm": "Zrušiť sledovanie",
|
"confirmations.unfollow.confirm": "Zrušiť sledovanie",
|
||||||
|
"confirmations.unfollow.title": "Prestať sledovať {name}?",
|
||||||
"content_warning.hide": "Skryť príspevok",
|
"content_warning.hide": "Skryť príspevok",
|
||||||
"content_warning.show": "Aj tak zobraziť",
|
"content_warning.show": "Aj tak zobraziť",
|
||||||
"content_warning.show_more": "Ukázať viac",
|
"content_warning.show_more": "Ukázať viac",
|
||||||
@ -277,6 +286,7 @@
|
|||||||
"domain_pill.server": "Server",
|
"domain_pill.server": "Server",
|
||||||
"domain_pill.their_server": "Ich digitálny domov, kde žijú všetky ich príspevky.",
|
"domain_pill.their_server": "Ich digitálny domov, kde žijú všetky ich príspevky.",
|
||||||
"domain_pill.username": "Používateľské meno",
|
"domain_pill.username": "Používateľské meno",
|
||||||
|
"dropdown.empty": "Vyber možnosť",
|
||||||
"embed.instructions": "Tento príspevok môžete pridať na svoju webovú stránku použitím tohto kódu.",
|
"embed.instructions": "Tento príspevok môžete pridať na svoju webovú stránku použitím tohto kódu.",
|
||||||
"embed.preview": "Takto bude vyzerať:",
|
"embed.preview": "Takto bude vyzerať:",
|
||||||
"emoji_button.activity": "Aktivita",
|
"emoji_button.activity": "Aktivita",
|
||||||
@ -377,6 +387,7 @@
|
|||||||
"getting_started.heading": "Začíname",
|
"getting_started.heading": "Začíname",
|
||||||
"hashtag.admin_moderation": "Otvor moderovacie rozhranie pre #{name}",
|
"hashtag.admin_moderation": "Otvor moderovacie rozhranie pre #{name}",
|
||||||
"hashtag.browse": "Prehľadávať príspevky pod #{hashtag}",
|
"hashtag.browse": "Prehľadávať príspevky pod #{hashtag}",
|
||||||
|
"hashtag.browse_from_account": "Prehľadávať príspevky od @{name} pre #{hashtag}",
|
||||||
"hashtag.column_header.tag_mode.all": "a {additional}",
|
"hashtag.column_header.tag_mode.all": "a {additional}",
|
||||||
"hashtag.column_header.tag_mode.any": "alebo {additional}",
|
"hashtag.column_header.tag_mode.any": "alebo {additional}",
|
||||||
"hashtag.column_header.tag_mode.none": "bez {additional}",
|
"hashtag.column_header.tag_mode.none": "bez {additional}",
|
||||||
@ -460,6 +471,7 @@
|
|||||||
"keyboard_shortcuts.translate": "Preložiť príspevok",
|
"keyboard_shortcuts.translate": "Preložiť príspevok",
|
||||||
"keyboard_shortcuts.unfocus": "Odísť z textového poľa",
|
"keyboard_shortcuts.unfocus": "Odísť z textového poľa",
|
||||||
"keyboard_shortcuts.up": "Posunúť sa vyššie v zozname",
|
"keyboard_shortcuts.up": "Posunúť sa vyššie v zozname",
|
||||||
|
"learn_more_link.learn_more": "Zistiť viac",
|
||||||
"lightbox.close": "Zatvoriť",
|
"lightbox.close": "Zatvoriť",
|
||||||
"lightbox.next": "Ďalej",
|
"lightbox.next": "Ďalej",
|
||||||
"lightbox.previous": "Späť",
|
"lightbox.previous": "Späť",
|
||||||
@ -542,6 +554,7 @@
|
|||||||
"notification.label.mention": "Označenie",
|
"notification.label.mention": "Označenie",
|
||||||
"notification.label.private_mention": "Súkromné označenie",
|
"notification.label.private_mention": "Súkromné označenie",
|
||||||
"notification.label.private_reply": "Súkromná odpoveď",
|
"notification.label.private_reply": "Súkromná odpoveď",
|
||||||
|
"notification.label.quote": "{name} citoval/a tvoj príspevok",
|
||||||
"notification.label.reply": "Odpoveď",
|
"notification.label.reply": "Odpoveď",
|
||||||
"notification.mention": "Označenie",
|
"notification.mention": "Označenie",
|
||||||
"notification.mentioned_you": "{name} ťa spomenul/a",
|
"notification.mentioned_you": "{name} ťa spomenul/a",
|
||||||
@ -556,6 +569,7 @@
|
|||||||
"notification.moderation_warning.action_suspend": "Tvoj účet bol pozastavený.",
|
"notification.moderation_warning.action_suspend": "Tvoj účet bol pozastavený.",
|
||||||
"notification.own_poll": "Vaša anketa sa skončila",
|
"notification.own_poll": "Vaša anketa sa skončila",
|
||||||
"notification.poll": "Anketa, v ktorej si hlasoval/a, skončila",
|
"notification.poll": "Anketa, v ktorej si hlasoval/a, skončila",
|
||||||
|
"notification.quoted_update": "{name} upravil/a príspevok, ktorý si citoval/a",
|
||||||
"notification.reblog": "{name} zdieľa váš príspevok",
|
"notification.reblog": "{name} zdieľa váš príspevok",
|
||||||
"notification.reblog.name_and_others_with_link": "{name} a <a>{count, plural, one {# ďalší človek} few {# ďalší ľudia} many {#} other {# ďalších ľudí}}</a> zdieľa váš príspevok",
|
"notification.reblog.name_and_others_with_link": "{name} a <a>{count, plural, one {# ďalší človek} few {# ďalší ľudia} many {#} other {# ďalších ľudí}}</a> zdieľa váš príspevok",
|
||||||
"notification.relationships_severance_event": "Stratené prepojenia s {name}",
|
"notification.relationships_severance_event": "Stratené prepojenia s {name}",
|
||||||
@ -585,6 +599,7 @@
|
|||||||
"notifications.column_settings.mention": "Označenia:",
|
"notifications.column_settings.mention": "Označenia:",
|
||||||
"notifications.column_settings.poll": "Výsledky ankety:",
|
"notifications.column_settings.poll": "Výsledky ankety:",
|
||||||
"notifications.column_settings.push": "Upozornenia push",
|
"notifications.column_settings.push": "Upozornenia push",
|
||||||
|
"notifications.column_settings.quote": "Citácie:",
|
||||||
"notifications.column_settings.reblog": "Zdieľania:",
|
"notifications.column_settings.reblog": "Zdieľania:",
|
||||||
"notifications.column_settings.show": "Zobraziť v stĺpci",
|
"notifications.column_settings.show": "Zobraziť v stĺpci",
|
||||||
"notifications.column_settings.sound": "Prehrať zvuk",
|
"notifications.column_settings.sound": "Prehrať zvuk",
|
||||||
@ -655,10 +670,16 @@
|
|||||||
"privacy.private.short": "Sledovatelia",
|
"privacy.private.short": "Sledovatelia",
|
||||||
"privacy.public.long": "Ktokoľvek na Mastodone aj mimo neho",
|
"privacy.public.long": "Ktokoľvek na Mastodone aj mimo neho",
|
||||||
"privacy.public.short": "Verejné",
|
"privacy.public.short": "Verejné",
|
||||||
|
"privacy.quote.anyone": "{visibility}, hocikto môže citovať",
|
||||||
|
"privacy.quote.disabled": "{visibility}, citovanie nepovolené",
|
||||||
|
"privacy.quote.limited": "{visibility}, citovanie obmedzené",
|
||||||
"privacy.unlisted.additional": "Presne ako verejné, s tým rozdielom, že sa príspevok nezobrazí v živých kanáloch, hashtagoch, objavovaní či vo vyhľadávaní na Mastodone, aj keď máte pre účet objaviteľnosť zapnutú.",
|
"privacy.unlisted.additional": "Presne ako verejné, s tým rozdielom, že sa príspevok nezobrazí v živých kanáloch, hashtagoch, objavovaní či vo vyhľadávaní na Mastodone, aj keď máte pre účet objaviteľnosť zapnutú.",
|
||||||
"privacy.unlisted.short": "Tiché verejné",
|
"privacy.unlisted.short": "Tiché verejné",
|
||||||
"privacy_policy.last_updated": "Posledná úprava {date}",
|
"privacy_policy.last_updated": "Posledná úprava {date}",
|
||||||
"privacy_policy.title": "Pravidlá ochrany súkromia",
|
"privacy_policy.title": "Pravidlá ochrany súkromia",
|
||||||
|
"quote_error.poll": "Citovanie ankiet nieje povolené.",
|
||||||
|
"quote_error.quote": "Je povolená iba jedna citácia súčasne.",
|
||||||
|
"quote_error.unauthorized": "Nemáš oprávnenie citovať tento príspevok.",
|
||||||
"recommended": "Odporúčané",
|
"recommended": "Odporúčané",
|
||||||
"refresh": "Obnoviť",
|
"refresh": "Obnoviť",
|
||||||
"regeneration_indicator.please_stand_by": "Prosím, čakajte.",
|
"regeneration_indicator.please_stand_by": "Prosím, čakajte.",
|
||||||
@ -674,6 +695,7 @@
|
|||||||
"relative_time.minutes": "{number} min",
|
"relative_time.minutes": "{number} min",
|
||||||
"relative_time.seconds": "{number} sek",
|
"relative_time.seconds": "{number} sek",
|
||||||
"relative_time.today": "Dnes",
|
"relative_time.today": "Dnes",
|
||||||
|
"remove_quote_hint.title": "Chceš vymazať svoju citáciu príspevku?",
|
||||||
"reply_indicator.attachments": "{count, plural, one {# príloha} few {# prílohy} other {# príloh}}",
|
"reply_indicator.attachments": "{count, plural, one {# príloha} few {# prílohy} other {# príloh}}",
|
||||||
"reply_indicator.cancel": "Zrušiť",
|
"reply_indicator.cancel": "Zrušiť",
|
||||||
"reply_indicator.poll": "Anketa",
|
"reply_indicator.poll": "Anketa",
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
"account.edit_profile_short": "編輯",
|
"account.edit_profile_short": "編輯",
|
||||||
"account.enable_notifications": "當 @{name} 嘟文時通知我",
|
"account.enable_notifications": "當 @{name} 嘟文時通知我",
|
||||||
"account.endorse": "於個人檔案推薦對方",
|
"account.endorse": "於個人檔案推薦對方",
|
||||||
"account.familiar_followers_many": "被 {name1}、{name2}、及 {othersCount, plural, other {其他您認識的 # 人}} 跟隨",
|
"account.familiar_followers_many": "被 {name1}、{name2}、及{othersCount, plural, other {其他您認識的 # 人}} 跟隨",
|
||||||
"account.familiar_followers_one": "被 {name1} 跟隨",
|
"account.familiar_followers_one": "被 {name1} 跟隨",
|
||||||
"account.familiar_followers_two": "被 {name1} 與 {name2} 跟隨",
|
"account.familiar_followers_two": "被 {name1} 與 {name2} 跟隨",
|
||||||
"account.featured": "精選內容",
|
"account.featured": "精選內容",
|
||||||
@ -48,10 +48,10 @@
|
|||||||
"account.follow_request_short": "跟隨請求",
|
"account.follow_request_short": "跟隨請求",
|
||||||
"account.followers": "跟隨者",
|
"account.followers": "跟隨者",
|
||||||
"account.followers.empty": "尚未有人跟隨這位使用者。",
|
"account.followers.empty": "尚未有人跟隨這位使用者。",
|
||||||
"account.followers_counter": "被 {count, plural, other {{count} 人}}跟隨",
|
"account.followers_counter": "被 {count, plural, other {{counter} 人}}跟隨",
|
||||||
"account.followers_you_know_counter": "{counter} 位您知道的跟隨者",
|
"account.followers_you_know_counter": "{counter} 位您知道的跟隨者",
|
||||||
"account.following": "跟隨中",
|
"account.following": "跟隨中",
|
||||||
"account.following_counter": "正在跟隨 {count,plural,other {{count} 人}}",
|
"account.following_counter": "正在跟隨 {count,plural,other {{counter} 人}}",
|
||||||
"account.follows.empty": "這位使用者尚未跟隨任何人。",
|
"account.follows.empty": "這位使用者尚未跟隨任何人。",
|
||||||
"account.follows_you": "已跟隨您",
|
"account.follows_you": "已跟隨您",
|
||||||
"account.go_to_profile": "前往個人檔案",
|
"account.go_to_profile": "前往個人檔案",
|
||||||
@ -80,7 +80,7 @@
|
|||||||
"account.requests_to_follow_you": "要求跟隨您",
|
"account.requests_to_follow_you": "要求跟隨您",
|
||||||
"account.share": "分享 @{name} 的個人檔案",
|
"account.share": "分享 @{name} 的個人檔案",
|
||||||
"account.show_reblogs": "顯示來自 @{name} 的轉嘟",
|
"account.show_reblogs": "顯示來自 @{name} 的轉嘟",
|
||||||
"account.statuses_counter": "{count, plural, other {{count} 則嘟文}}",
|
"account.statuses_counter": "{count, plural, other {{counter} 則嘟文}}",
|
||||||
"account.unblock": "解除封鎖 @{name}",
|
"account.unblock": "解除封鎖 @{name}",
|
||||||
"account.unblock_domain": "解除封鎖網域 {domain}",
|
"account.unblock_domain": "解除封鎖網域 {domain}",
|
||||||
"account.unblock_domain_short": "解除封鎖",
|
"account.unblock_domain_short": "解除封鎖",
|
||||||
@ -104,9 +104,9 @@
|
|||||||
"alert.rate_limited.title": "已限速",
|
"alert.rate_limited.title": "已限速",
|
||||||
"alert.unexpected.message": "發生非預期的錯誤。",
|
"alert.unexpected.message": "發生非預期的錯誤。",
|
||||||
"alert.unexpected.title": "哎呀!",
|
"alert.unexpected.title": "哎呀!",
|
||||||
"alt_text_badge.title": "ALT 說明文字",
|
"alt_text_badge.title": "替代文字",
|
||||||
"alt_text_modal.add_alt_text": "新增 ALT 說明文字",
|
"alt_text_modal.add_alt_text": "新增替代文字",
|
||||||
"alt_text_modal.add_text_from_image": "自圖片新增 ALT 說明文字",
|
"alt_text_modal.add_text_from_image": "自圖片新增替代文字",
|
||||||
"alt_text_modal.cancel": "取消",
|
"alt_text_modal.cancel": "取消",
|
||||||
"alt_text_modal.change_thumbnail": "變更預覽圖",
|
"alt_text_modal.change_thumbnail": "變更預覽圖",
|
||||||
"alt_text_modal.describe_for_people_with_hearing_impairments": "替聽覺障礙人士描述...",
|
"alt_text_modal.describe_for_people_with_hearing_impairments": "替聽覺障礙人士描述...",
|
||||||
@ -244,10 +244,10 @@
|
|||||||
"confirmations.logout.confirm": "登出",
|
"confirmations.logout.confirm": "登出",
|
||||||
"confirmations.logout.message": "您確定要登出嗎?",
|
"confirmations.logout.message": "您確定要登出嗎?",
|
||||||
"confirmations.logout.title": "您確定要登出嗎?",
|
"confirmations.logout.title": "您確定要登出嗎?",
|
||||||
"confirmations.missing_alt_text.confirm": "新增 ALT 說明文字",
|
"confirmations.missing_alt_text.confirm": "新增替代文字",
|
||||||
"confirmations.missing_alt_text.message": "您的嘟文中的多媒體內容未附上 ALT 說明文字。添加說明文字描述能幫助更多人存取您的內容。",
|
"confirmations.missing_alt_text.message": "您的嘟文中的多媒體內容未附上替代文字。添加描述能幫助更多人存取您的內容。",
|
||||||
"confirmations.missing_alt_text.secondary": "仍要發嘟",
|
"confirmations.missing_alt_text.secondary": "仍要發嘟",
|
||||||
"confirmations.missing_alt_text.title": "是否新增 ALT 說明文字?",
|
"confirmations.missing_alt_text.title": "是否新增替代文字?",
|
||||||
"confirmations.mute.confirm": "靜音",
|
"confirmations.mute.confirm": "靜音",
|
||||||
"confirmations.private_quote_notify.cancel": "返回至編輯",
|
"confirmations.private_quote_notify.cancel": "返回至編輯",
|
||||||
"confirmations.private_quote_notify.confirm": "發表嘟文",
|
"confirmations.private_quote_notify.confirm": "發表嘟文",
|
||||||
@ -435,9 +435,9 @@
|
|||||||
"hashtag.column_settings.tag_mode.any": "任一",
|
"hashtag.column_settings.tag_mode.any": "任一",
|
||||||
"hashtag.column_settings.tag_mode.none": "全不",
|
"hashtag.column_settings.tag_mode.none": "全不",
|
||||||
"hashtag.column_settings.tag_toggle": "將額外標籤加入到這個欄位",
|
"hashtag.column_settings.tag_toggle": "將額外標籤加入到這個欄位",
|
||||||
"hashtag.counter_by_accounts": "{count, plural, one {{counter} 名} other {{counter} 名}}參與者",
|
"hashtag.counter_by_accounts": "{count, plural, other {{counter} 名參與者}}",
|
||||||
"hashtag.counter_by_uses": "{count, plural, one {{counter} 則} other {{counter} 則}}嘟文",
|
"hashtag.counter_by_uses": "{count, plural, other {{counter} 則嘟文}}",
|
||||||
"hashtag.counter_by_uses_today": "本日有 {count, plural, one {{counter} 則} other {{counter} 則}}嘟文",
|
"hashtag.counter_by_uses_today": "本日有 {count, plural, other {{counter} 則嘟文}}",
|
||||||
"hashtag.feature": "於個人檔案推薦",
|
"hashtag.feature": "於個人檔案推薦",
|
||||||
"hashtag.follow": "跟隨主題標籤",
|
"hashtag.follow": "跟隨主題標籤",
|
||||||
"hashtag.mute": "靜音 #{hashtag}",
|
"hashtag.mute": "靜音 #{hashtag}",
|
||||||
@ -470,7 +470,7 @@
|
|||||||
"ignore_notifications_modal.not_following_title": "忽略來自您未跟隨帳號之推播通知?",
|
"ignore_notifications_modal.not_following_title": "忽略來自您未跟隨帳號之推播通知?",
|
||||||
"ignore_notifications_modal.private_mentions_title": "忽略來自不請自來私訊之推播通知?",
|
"ignore_notifications_modal.private_mentions_title": "忽略來自不請自來私訊之推播通知?",
|
||||||
"info_button.label": "幫助",
|
"info_button.label": "幫助",
|
||||||
"info_button.what_is_alt_text": "<h1>何謂 ALT 說明文字?</h1> <p>ALT 說明文字為視覺障礙者、低網路頻寬或尋求額外上下文語境的人們提供圖片描述。</p> <p>您可以透過撰寫清晰、簡潔及客觀的說明文字以替所有人改善無障礙特性與協助理解。</p> <ul> <li>掌握幾個重要元素</li> <li>替圖片提供文字摘要</li> <li>使用常規行文結構</li> <li>避免冗贅資訊</li> <li>聚焦於趨勢與複雜視覺中之關鍵(如圖表或地圖)</li> </ul>",
|
"info_button.what_is_alt_text": "<h1>何謂替代文字?</h1> <p>替代文字為視覺障礙者、低網路頻寬或尋求額外上下文語境的人們提供圖片描述。</p> <p>您可以透過撰寫清晰、簡潔及客觀的替代文字以替所有人改善無障礙特性與協助理解。</p> <ul> <li>掌握幾個重要元素</li> <li>替圖片提供文字摘要</li> <li>使用常規行文結構</li> <li>避免冗贅資訊</li> <li>聚焦於趨勢與複雜視覺中之關鍵(如圖表或地圖)</li> </ul>",
|
||||||
"interaction_modal.action": "若欲與 {name} 之嘟文互動,您必須登入您帳號所註冊之 Mastodon 伺服器。",
|
"interaction_modal.action": "若欲與 {name} 之嘟文互動,您必須登入您帳號所註冊之 Mastodon 伺服器。",
|
||||||
"interaction_modal.go": "Go!",
|
"interaction_modal.go": "Go!",
|
||||||
"interaction_modal.no_account_yet": "仍尚未有帳號嗎?",
|
"interaction_modal.no_account_yet": "仍尚未有帳號嗎?",
|
||||||
@ -529,7 +529,7 @@
|
|||||||
"limited_account_hint.title": "此個人檔案已被 {domain} 的管理員隱藏。",
|
"limited_account_hint.title": "此個人檔案已被 {domain} 的管理員隱藏。",
|
||||||
"link_preview.author": "來自 {name}",
|
"link_preview.author": "來自 {name}",
|
||||||
"link_preview.more_from_author": "來自 {name} 之更多內容",
|
"link_preview.more_from_author": "來自 {name} 之更多內容",
|
||||||
"link_preview.shares": "{count, plural, other {{count} 則嘟文}}",
|
"link_preview.shares": "{count, plural, other {{counter} 則嘟文}}",
|
||||||
"lists.add_member": "新增",
|
"lists.add_member": "新增",
|
||||||
"lists.add_to_list": "新增至列表",
|
"lists.add_to_list": "新增至列表",
|
||||||
"lists.add_to_lists": "新增 {name} 至列表",
|
"lists.add_to_lists": "新增 {name} 至列表",
|
||||||
@ -561,7 +561,7 @@
|
|||||||
"moved_to_account_banner.text": "您的帳號 {disabledAccount} 目前已停用,因為您已搬家至 {movedToAccount}。",
|
"moved_to_account_banner.text": "您的帳號 {disabledAccount} 目前已停用,因為您已搬家至 {movedToAccount}。",
|
||||||
"mute_modal.hide_from_notifications": "於推播通知中隱藏",
|
"mute_modal.hide_from_notifications": "於推播通知中隱藏",
|
||||||
"mute_modal.hide_options": "隱藏選項",
|
"mute_modal.hide_options": "隱藏選項",
|
||||||
"mute_modal.indefinite": "直到我解除靜音他們",
|
"mute_modal.indefinite": "直到我解除靜音",
|
||||||
"mute_modal.show_options": "顯示選項",
|
"mute_modal.show_options": "顯示選項",
|
||||||
"mute_modal.they_can_mention_and_follow": "他們仍可提及或跟隨您,但您不會見到他們。",
|
"mute_modal.they_can_mention_and_follow": "他們仍可提及或跟隨您,但您不會見到他們。",
|
||||||
"mute_modal.they_wont_know": "他們不會知道他們已被靜音。",
|
"mute_modal.they_wont_know": "他們不會知道他們已被靜音。",
|
||||||
@ -649,10 +649,10 @@
|
|||||||
"notification_requests.accept_multiple": "{count, plural, other {接受 # 則請求...}}",
|
"notification_requests.accept_multiple": "{count, plural, other {接受 # 則請求...}}",
|
||||||
"notification_requests.confirm_accept_multiple.button": "{count, plural, other {接受請求}}",
|
"notification_requests.confirm_accept_multiple.button": "{count, plural, other {接受請求}}",
|
||||||
"notification_requests.confirm_accept_multiple.message": "您將接受 {count, plural, other {# 則推播通知請求}}。您確定要繼續?",
|
"notification_requests.confirm_accept_multiple.message": "您將接受 {count, plural, other {# 則推播通知請求}}。您確定要繼續?",
|
||||||
"notification_requests.confirm_accept_multiple.title": "接受推播通知請求?",
|
"notification_requests.confirm_accept_multiple.title": "是否接受推播通知請求?",
|
||||||
"notification_requests.confirm_dismiss_multiple.button": "{count, plural, other {忽略請求}}",
|
"notification_requests.confirm_dismiss_multiple.button": "{count, plural, other {忽略請求}}",
|
||||||
"notification_requests.confirm_dismiss_multiple.message": "您將忽略 {count, plural, other {# 則推播通知請求}}。您將不再能輕易存取{count, plural, other {這些}}推播通知。您確定要繼續?",
|
"notification_requests.confirm_dismiss_multiple.message": "您將忽略 {count, plural, other {# 則推播通知請求}}。您將不再能輕易存取{count, plural, other {這些}}推播通知。您確定要繼續?",
|
||||||
"notification_requests.confirm_dismiss_multiple.title": "忽略推播通知請求?",
|
"notification_requests.confirm_dismiss_multiple.title": "是否忽略推播通知請求?",
|
||||||
"notification_requests.dismiss": "關閉",
|
"notification_requests.dismiss": "關閉",
|
||||||
"notification_requests.dismiss_multiple": "{count, plural, other {忽略 # 則請求...}}",
|
"notification_requests.dismiss_multiple": "{count, plural, other {忽略 # 則請求...}}",
|
||||||
"notification_requests.edit_selection": "編輯",
|
"notification_requests.edit_selection": "編輯",
|
||||||
@ -710,7 +710,7 @@
|
|||||||
"notifications.policy.filter_limited_accounts_title": "受管制帳號",
|
"notifications.policy.filter_limited_accounts_title": "受管制帳號",
|
||||||
"notifications.policy.filter_new_accounts.hint": "新增於過去 {days, plural, other {# 日}}",
|
"notifications.policy.filter_new_accounts.hint": "新增於過去 {days, plural, other {# 日}}",
|
||||||
"notifications.policy.filter_new_accounts_title": "新帳號",
|
"notifications.policy.filter_new_accounts_title": "新帳號",
|
||||||
"notifications.policy.filter_not_followers_hint": "包含最近 {days, plural, other {# 日}} 內跟隨您之使用者",
|
"notifications.policy.filter_not_followers_hint": "包含最近 {days, plural, other {# 日}}內跟隨您之使用者",
|
||||||
"notifications.policy.filter_not_followers_title": "未跟隨您之使用者",
|
"notifications.policy.filter_not_followers_title": "未跟隨您之使用者",
|
||||||
"notifications.policy.filter_not_following_hint": "直至您手動核准他們",
|
"notifications.policy.filter_not_following_hint": "直至您手動核准他們",
|
||||||
"notifications.policy.filter_not_following_title": "您未跟隨之使用者",
|
"notifications.policy.filter_not_following_title": "您未跟隨之使用者",
|
||||||
@ -830,7 +830,7 @@
|
|||||||
"report.thanks.title_actionable": "感謝您的檢舉,我們將會著手處理。",
|
"report.thanks.title_actionable": "感謝您的檢舉,我們將會著手處理。",
|
||||||
"report.unfollow": "取消跟隨 @{name}",
|
"report.unfollow": "取消跟隨 @{name}",
|
||||||
"report.unfollow_explanation": "您正在跟隨此帳號。如不欲於首頁時間軸再見到他們的嘟文,請取消跟隨。",
|
"report.unfollow_explanation": "您正在跟隨此帳號。如不欲於首頁時間軸再見到他們的嘟文,請取消跟隨。",
|
||||||
"report_notification.attached_statuses": "{count, plural, one {{count} 則} other {{count} 則}} 嘟文",
|
"report_notification.attached_statuses": "已附加 {count, plural, other {{count} 則嘟文}}",
|
||||||
"report_notification.categories.legal": "合法性",
|
"report_notification.categories.legal": "合法性",
|
||||||
"report_notification.categories.legal_sentence": "違法內容",
|
"report_notification.categories.legal_sentence": "違法內容",
|
||||||
"report_notification.categories.other": "其他",
|
"report_notification.categories.other": "其他",
|
||||||
@ -900,7 +900,7 @@
|
|||||||
"status.direct_indicator": "私訊",
|
"status.direct_indicator": "私訊",
|
||||||
"status.edit": "編輯",
|
"status.edit": "編輯",
|
||||||
"status.edited": "上次編輯於 {date}",
|
"status.edited": "上次編輯於 {date}",
|
||||||
"status.edited_x_times": "已編輯 {count, plural, one {{count} 次} other {{count} 次}}",
|
"status.edited_x_times": "已編輯 {count, plural, other {{count} 次}}",
|
||||||
"status.embed": "取得嵌入程式碼",
|
"status.embed": "取得嵌入程式碼",
|
||||||
"status.favourite": "最愛",
|
"status.favourite": "最愛",
|
||||||
"status.favourites": "{count, plural, other {則最愛}}",
|
"status.favourites": "{count, plural, other {則最愛}}",
|
||||||
@ -980,11 +980,11 @@
|
|||||||
"terms_of_service.title": "服務條款",
|
"terms_of_service.title": "服務條款",
|
||||||
"terms_of_service.upcoming_changes_on": "{date} 起即將發生之異動",
|
"terms_of_service.upcoming_changes_on": "{date} 起即將發生之異動",
|
||||||
"time_remaining.days": "剩餘 {number, plural, other {# 天}}",
|
"time_remaining.days": "剩餘 {number, plural, other {# 天}}",
|
||||||
"time_remaining.hours": "剩餘{number, plural, other {# 小時}}",
|
"time_remaining.hours": "剩餘 {number, plural, other {# 小時}}",
|
||||||
"time_remaining.minutes": "剩餘{number, plural, other {# 分鐘}}",
|
"time_remaining.minutes": "剩餘 {number, plural, other {# 分鐘}}",
|
||||||
"time_remaining.moments": "剩餘時間",
|
"time_remaining.moments": "剩餘時間",
|
||||||
"time_remaining.seconds": "剩餘{number, plural, other {# 秒}}",
|
"time_remaining.seconds": "剩餘 {number, plural, other {# 秒}}",
|
||||||
"trends.counter_by_accounts": "{count, plural, one {{counter} 人} other {{counter} 人}}於過去 {days, plural, one {日} other {{days} 日}} 之間",
|
"trends.counter_by_accounts": "{count, plural, other {{counter} 人}}於過去 {days, plural, other {{days} 日}}之間",
|
||||||
"trends.trending_now": "現正熱門趨勢",
|
"trends.trending_now": "現正熱門趨勢",
|
||||||
"ui.beforeunload": "如果離開 Mastodon,您的草稿將會不見。",
|
"ui.beforeunload": "如果離開 Mastodon,您的草稿將會不見。",
|
||||||
"units.short.billion": "{count}B",
|
"units.short.billion": "{count}B",
|
||||||
@ -1015,7 +1015,7 @@
|
|||||||
"video.play": "播放",
|
"video.play": "播放",
|
||||||
"video.skip_backward": "上一部",
|
"video.skip_backward": "上一部",
|
||||||
"video.skip_forward": "下一部",
|
"video.skip_forward": "下一部",
|
||||||
"video.unmute": "取消靜音",
|
"video.unmute": "解除靜音",
|
||||||
"video.volume_down": "降低音量",
|
"video.volume_down": "降低音量",
|
||||||
"video.volume_up": "提高音量",
|
"video.volume_up": "提高音量",
|
||||||
"visibility_modal.button_title": "設定可見性",
|
"visibility_modal.button_title": "設定可見性",
|
||||||
|
|||||||
@ -3181,20 +3181,21 @@ a.account__display-name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.column__alert {
|
.column__alert {
|
||||||
|
--alert-height: 54px;
|
||||||
|
|
||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
grid-template-rows: minmax(var(--alert-height), max-content);
|
||||||
|
align-items: end;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 360px;
|
max-width: 360px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin: auto auto 0;
|
margin: auto auto 0;
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
|
pointer-events: none;
|
||||||
&:empty {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: #{$mobile-menu-breakpoint - 1}) {
|
@media (max-width: #{$mobile-menu-breakpoint - 1}) {
|
||||||
// Compensate for mobile menubar
|
// Compensate for mobile menubar
|
||||||
@ -3205,6 +3206,7 @@ a.account__display-name {
|
|||||||
// Make all nested alerts occupy the same space
|
// Make all nested alerts occupy the same space
|
||||||
// rather than stack
|
// rather than stack
|
||||||
grid-area: 1 / 1;
|
grid-area: 1 / 1;
|
||||||
|
pointer-events: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4484,13 +4486,19 @@ a.status-card {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
&:hover {
|
&--large {
|
||||||
background: var(--on-surface-color);
|
padding-block: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:is(button) {
|
||||||
outline: 2px solid $ui-button-focus-outline-color;
|
&:hover {
|
||||||
outline-offset: -2px;
|
background: var(--on-surface-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 2px solid $ui-button-focus-outline-color;
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@ -6196,6 +6204,8 @@ a.status-card {
|
|||||||
inset-inline-start: 0;
|
inset-inline-start: 0;
|
||||||
inset-inline-end: 0;
|
inset-inline-end: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around; // If set to center, the fullscreen image overlay is misaligned.
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|||||||
@ -1750,6 +1750,7 @@ be:
|
|||||||
disabled_account: Пасля гэтага ваш бягучы ўліковы запіс не будзе цалкам даступны. Аднак у вас будзе доступ да экспарту даных, а таксама да паўторнай актывацыі.
|
disabled_account: Пасля гэтага ваш бягучы ўліковы запіс не будзе цалкам даступны. Аднак у вас будзе доступ да экспарту даных, а таксама да паўторнай актывацыі.
|
||||||
followers: Гэтае дзеянне будзе «пераносіць» усіх падпісчыкаў з бягучага ўліковага запісу на новы
|
followers: Гэтае дзеянне будзе «пераносіць» усіх падпісчыкаў з бягучага ўліковага запісу на новы
|
||||||
only_redirect_html: Альбо вы можаце <a href="%{path}"> толькі наладзіць перанакіраванне на ваш профіль</a>.
|
only_redirect_html: Альбо вы можаце <a href="%{path}"> толькі наладзіць перанакіраванне на ваш профіль</a>.
|
||||||
|
other_data: Сістэма не будзе аўтаматычна перамяшчаць астатнія даныя (у тым ліку Вашы допісы і спіс уліковых запісаў, на якія Вы падпісаліся)
|
||||||
redirect: Профіль вашага бягучага ўліковага запісу будзе абноўлены з паведамленнем аб перанакіраванні і выключаны з пошуку
|
redirect: Профіль вашага бягучага ўліковага запісу будзе абноўлены з паведамленнем аб перанакіраванні і выключаны з пошуку
|
||||||
moderation:
|
moderation:
|
||||||
title: Мадэрацыя
|
title: Мадэрацыя
|
||||||
|
|||||||
@ -1750,6 +1750,7 @@ cs:
|
|||||||
disabled_account: Váš aktuální účet nebude poté zcela použitelný. Budete však mít přístup k datovým exportům a budete ho moci znovu aktivovat.
|
disabled_account: Váš aktuální účet nebude poté zcela použitelný. Budete však mít přístup k datovým exportům a budete ho moci znovu aktivovat.
|
||||||
followers: Touto akcí přesunete všechny sledující z aktuálního účtu na nový
|
followers: Touto akcí přesunete všechny sledující z aktuálního účtu na nový
|
||||||
only_redirect_html: Alternativně můžete <a href="%{path}">nastavit pouze přesměrování na váš profil</a>.
|
only_redirect_html: Alternativně můžete <a href="%{path}">nastavit pouze přesměrování na váš profil</a>.
|
||||||
|
other_data: Žádná další data nebudou automaticky přesunuta (včetně vašich příspěvků a seznamu účtů, které sledujete)
|
||||||
redirect: Profil vašeho aktuálního účtu bude aktualizován s oznámením o přesměrování a bude vyloučen z výsledků hledání
|
redirect: Profil vašeho aktuálního účtu bude aktualizován s oznámením o přesměrování a bude vyloučen z výsledků hledání
|
||||||
moderation:
|
moderation:
|
||||||
title: Moderování
|
title: Moderování
|
||||||
|
|||||||
@ -1828,6 +1828,7 @@ cy:
|
|||||||
disabled_account: Ni fydd modd defnyddio'ch cyfrif cyfredol yn llawn wedyn. Fodd bynnag, bydd gennych fynediad i allforio data yn ogystal ag ail agor.
|
disabled_account: Ni fydd modd defnyddio'ch cyfrif cyfredol yn llawn wedyn. Fodd bynnag, bydd gennych fynediad i allforio data yn ogystal ag ail agor.
|
||||||
followers: Bydd y weithred hon yn symud yr holl ddilynwyr o'r cyfrif cyfredol i'r cyfrif newydd
|
followers: Bydd y weithred hon yn symud yr holl ddilynwyr o'r cyfrif cyfredol i'r cyfrif newydd
|
||||||
only_redirect_html: Fel arall, <a href="%{path}">dim ond ailgyfeiriad y gallwch chi ei osod ar eich proffil</a>.
|
only_redirect_html: Fel arall, <a href="%{path}">dim ond ailgyfeiriad y gallwch chi ei osod ar eich proffil</a>.
|
||||||
|
other_data: Bydd dim ddata arall yn cael ei symud yn awtomatig (gan gynnwys eich postiadau a'r rhestr o gyfrifon rydych chi'n eu dilyn)
|
||||||
redirect: Bydd proffil eich cyfrif presennol yn cael ei diweddaru gyda hysbysiad ailgyfeirio ac yn cael ei eithrio o chwiliadau
|
redirect: Bydd proffil eich cyfrif presennol yn cael ei diweddaru gyda hysbysiad ailgyfeirio ac yn cael ei eithrio o chwiliadau
|
||||||
moderation:
|
moderation:
|
||||||
title: Cymedroil
|
title: Cymedroil
|
||||||
@ -2100,6 +2101,7 @@ cy:
|
|||||||
errors:
|
errors:
|
||||||
in_reply_not_found: Nid yw'n ymddangos bod y postiad rydych chi'n ceisio ei ateb yn bodoli.
|
in_reply_not_found: Nid yw'n ymddangos bod y postiad rydych chi'n ceisio ei ateb yn bodoli.
|
||||||
quoted_status_not_found: Nid yw'n ymddangos bod y postiad rydych chi'n ceisio'i ddyfynnu yn bodoli.
|
quoted_status_not_found: Nid yw'n ymddangos bod y postiad rydych chi'n ceisio'i ddyfynnu yn bodoli.
|
||||||
|
quoted_user_not_mentioned: Does dim modd dyfynnu defnyddiwr heb ei grybwyll mewn postiad Crybwyll Preifat.
|
||||||
over_character_limit: wedi mynd y tu hwnt i'r terfyn nodau o %{max}
|
over_character_limit: wedi mynd y tu hwnt i'r terfyn nodau o %{max}
|
||||||
pin_errors:
|
pin_errors:
|
||||||
direct: Nid oes modd pinio postiadau sy'n weladwy i ddefnyddwyr a grybwyllwyd yn unig
|
direct: Nid oes modd pinio postiadau sy'n weladwy i ddefnyddwyr a grybwyllwyd yn unig
|
||||||
|
|||||||
@ -1672,6 +1672,7 @@ de:
|
|||||||
disabled_account: Dein altes Konto ist nur noch eingeschränkt verwendbar. Du kannst jedoch deine Daten exportieren und das Konto wieder reaktivieren.
|
disabled_account: Dein altes Konto ist nur noch eingeschränkt verwendbar. Du kannst jedoch deine Daten exportieren und das Konto wieder reaktivieren.
|
||||||
followers: Alle Follower werden vom alten zum neuen Konto übertragen
|
followers: Alle Follower werden vom alten zum neuen Konto übertragen
|
||||||
only_redirect_html: Alternativ kannst du auch <a href="%{path}">nur eine Weiterleitung zu deinem neuen Konto</a> einrichten, ohne die Follower zu übertragen.
|
only_redirect_html: Alternativ kannst du auch <a href="%{path}">nur eine Weiterleitung zu deinem neuen Konto</a> einrichten, ohne die Follower zu übertragen.
|
||||||
|
other_data: Es werden keine weiteren Daten automatisch übertragen (einschließlich deiner Beiträge und der Liste der Konten, denen du folgst)
|
||||||
redirect: Dein altes Konto wird einen Hinweis erhalten, dass du umgezogen bist. Außerdem wird das Profil von Suchanfragen ausgeschlossen
|
redirect: Dein altes Konto wird einen Hinweis erhalten, dass du umgezogen bist. Außerdem wird das Profil von Suchanfragen ausgeschlossen
|
||||||
moderation:
|
moderation:
|
||||||
title: Moderation
|
title: Moderation
|
||||||
|
|||||||
@ -517,13 +517,13 @@ es:
|
|||||||
title: Fediverse Auxiliary Service Providers (Proveedores de Servicio Auxiliares del Fediverso)
|
title: Fediverse Auxiliary Service Providers (Proveedores de Servicio Auxiliares del Fediverso)
|
||||||
title: FASP
|
title: FASP
|
||||||
follow_recommendations:
|
follow_recommendations:
|
||||||
description_html: "<strong>Las recomendaciones de cuentas ayudan a los nuevos usuarios a encontrar rápidamente contenido interesante</strong>. Cuando un usuario no ha interactuado con otros lo suficiente como para suscitar recomendaciones personalizadas de cuentas a las que seguir, en su lugar se le recomiendan estas cuentas. Se recalculan diariamente a partir de una mezcla de cuentas con el mayor número de interacciones recientes y con el mayor número de seguidores locales con un idioma determinado."
|
description_html: "<strong>Las recomendaciones de cuentas a las que seguir ayudan a los nuevos usuarios a encontrar rápidamente contenido interesante</strong>. Cuando un usuario no ha interactuado con otros lo suficiente como para formar recomendaciones personalizadas de seguimiento, estas cuentas se recomiendan en su lugar. Se recalculan diariamente a partir de una mezcla de cuentas con las interacciones más recientes y el mayor número de seguidores para un idioma determinado."
|
||||||
language: Para el idioma
|
language: Para el idioma
|
||||||
status: Estado
|
status: Estado
|
||||||
suppress: Suprimir recomendación de cuentas
|
suppress: Eliminar recomendación de cuentas a las que seguir
|
||||||
suppressed: Suprimida
|
suppressed: Suprimida
|
||||||
title: Recomendaciones de cuentas
|
title: Recomendaciones de cuentas a las que seguir
|
||||||
unsuppress: Restaurar recomendaciones de cuentas
|
unsuppress: Restaurar recomendaciones de cuentas a las que seguir
|
||||||
instances:
|
instances:
|
||||||
audit_log:
|
audit_log:
|
||||||
title: Registros de auditoría recientes
|
title: Registros de auditoría recientes
|
||||||
|
|||||||
@ -1672,6 +1672,7 @@ it:
|
|||||||
disabled_account: Il tuo account attuale non sarà più pienamente utilizzabile. Tuttavia, avrai accesso all'esportazione dei dati e alla riattivazione.
|
disabled_account: Il tuo account attuale non sarà più pienamente utilizzabile. Tuttavia, avrai accesso all'esportazione dei dati e alla riattivazione.
|
||||||
followers: Questa azione sposterà tutti i follower dall'account attuale al nuovo account
|
followers: Questa azione sposterà tutti i follower dall'account attuale al nuovo account
|
||||||
only_redirect_html: In alternativa, puoi solo <a href="%{path}">impostare un reindirizzamento sul tuo profilo</a>.
|
only_redirect_html: In alternativa, puoi solo <a href="%{path}">impostare un reindirizzamento sul tuo profilo</a>.
|
||||||
|
other_data: Nessun altro dato verrà trasferito automaticamente (compresi i tuoi post e l'elenco degli account che segui)
|
||||||
redirect: Il profilo del tuo account corrente sarà aggiornato con un avviso di ridirezione e sarà escluso dalle ricerche
|
redirect: Il profilo del tuo account corrente sarà aggiornato con un avviso di ridirezione e sarà escluso dalle ricerche
|
||||||
moderation:
|
moderation:
|
||||||
title: Moderazione
|
title: Moderazione
|
||||||
|
|||||||
@ -65,7 +65,6 @@ an:
|
|||||||
warn: Amagar lo conteniu filtrau dezaga d'una alvertencia mencionando lo titol d'o filtro
|
warn: Amagar lo conteniu filtrau dezaga d'una alvertencia mencionando lo titol d'o filtro
|
||||||
form_admin_settings:
|
form_admin_settings:
|
||||||
activity_api_enabled: Conteyo de publicacions locals, usuarios activos y nuevos rechistros en periodos semanals
|
activity_api_enabled: Conteyo de publicacions locals, usuarios activos y nuevos rechistros en periodos semanals
|
||||||
bootstrap_timeline_accounts: Estas cuentas amaneixerán en a parte superior d'as recomendacions d'os nuevos usuarios.
|
|
||||||
closed_registrations_message: Amostrau quan los rechistros son zarraus
|
closed_registrations_message: Amostrau quan los rechistros son zarraus
|
||||||
custom_css: Puetz aplicar estilos personalizaus a la versión web de Mastodon.
|
custom_css: Puetz aplicar estilos personalizaus a la versión web de Mastodon.
|
||||||
mascot: Reemplaza la ilustración en a interficie web abanzada.
|
mascot: Reemplaza la ilustración en a interficie web abanzada.
|
||||||
|
|||||||
@ -81,7 +81,6 @@ ar:
|
|||||||
activity_api_enabled: عدد المنشورات المحلية و المستخدمين الناشطين و التسجيلات الأسبوعية الجديدة
|
activity_api_enabled: عدد المنشورات المحلية و المستخدمين الناشطين و التسجيلات الأسبوعية الجديدة
|
||||||
app_icon: WEBP أو PNG أو GIF أو JPG. يتجاوز أيقونة التطبيق الافتراضية على الجوالات مع أيقونة مخصصة.
|
app_icon: WEBP أو PNG أو GIF أو JPG. يتجاوز أيقونة التطبيق الافتراضية على الجوالات مع أيقونة مخصصة.
|
||||||
backups_retention_period: للمستخدمين القدرة على إنشاء أرشيفات لمنشوراتهم لتحميلها في وقت لاحق. عند التعيين إلى قيمة موجبة، سيتم حذف هذه الأرشيف تلقائياً من وحدة تخزينك بعد عدد الأيام المحدد.
|
backups_retention_period: للمستخدمين القدرة على إنشاء أرشيفات لمنشوراتهم لتحميلها في وقت لاحق. عند التعيين إلى قيمة موجبة، سيتم حذف هذه الأرشيف تلقائياً من وحدة تخزينك بعد عدد الأيام المحدد.
|
||||||
bootstrap_timeline_accounts: سيتم تثبيت هذه الحسابات على قمة التوصيات للمستخدمين الجدد.
|
|
||||||
closed_registrations_message: ما سيعرض عند إغلاق التسجيلات
|
closed_registrations_message: ما سيعرض عند إغلاق التسجيلات
|
||||||
content_cache_retention_period: سيتم حذف جميع المنشورات من الخوادم الأخرى (بما في ذلك التعزيزات والردود) بعد عدد الأيام المحدد، دون أي تفاعل محلي للمستخدم مع هذه المنشورات. وهذا يشمل المنشورات التي قام المستخدم المحلي بوضع علامة عليها كإشارات مرجعية أو المفضلة. وسوف تختفي أيضا الإشارات الخاصة بين المستخدمين من المثيلات المختلفة ويستحيل استعادتها. والغرض من استخدام هذا الإعداد هو مثيلات الغرض الخاص ويفسد الكثير من توقعات المستخدمين عند تنفيذها للاستخدام لأغراض عامة.
|
content_cache_retention_period: سيتم حذف جميع المنشورات من الخوادم الأخرى (بما في ذلك التعزيزات والردود) بعد عدد الأيام المحدد، دون أي تفاعل محلي للمستخدم مع هذه المنشورات. وهذا يشمل المنشورات التي قام المستخدم المحلي بوضع علامة عليها كإشارات مرجعية أو المفضلة. وسوف تختفي أيضا الإشارات الخاصة بين المستخدمين من المثيلات المختلفة ويستحيل استعادتها. والغرض من استخدام هذا الإعداد هو مثيلات الغرض الخاص ويفسد الكثير من توقعات المستخدمين عند تنفيذها للاستخدام لأغراض عامة.
|
||||||
custom_css: يمكنك تطبيق أساليب مخصصة على نسخة الويب من ماستدون.
|
custom_css: يمكنك تطبيق أساليب مخصصة على نسخة الويب من ماستدون.
|
||||||
|
|||||||
@ -79,6 +79,7 @@ be:
|
|||||||
featured_tag:
|
featured_tag:
|
||||||
name: 'Вось некаторыя з хэштэгаў, якімі вы нядаўна карысталіся:'
|
name: 'Вось некаторыя з хэштэгаў, якімі вы нядаўна карысталіся:'
|
||||||
filters:
|
filters:
|
||||||
|
action: Выберыце, што трэба рабіць, калі допіс супадзе з фільтрам
|
||||||
actions:
|
actions:
|
||||||
blur: Схавайце медыя за знакам папярэджання, не хаваючы пры гэтым тэкст
|
blur: Схавайце медыя за знакам папярэджання, не хаваючы пры гэтым тэкст
|
||||||
hide: Поўнасцю схаваць адфільтраванае змесціва, дзейнічаць, нібы яго не існуе
|
hide: Поўнасцю схаваць адфільтраванае змесціва, дзейнічаць, нібы яго не існуе
|
||||||
@ -87,7 +88,7 @@ be:
|
|||||||
activity_api_enabled: Падлік лакальна апублікаваных пастоў, актыўных карыстальнікаў і новых рэгістрацый у тыдзень
|
activity_api_enabled: Падлік лакальна апублікаваных пастоў, актыўных карыстальнікаў і новых рэгістрацый у тыдзень
|
||||||
app_icon: WEBP, PNG, GIF ці JPG. Заменіце прадвызначаны значок праграмы на мабільных прыладах карыстальніцкім значком.
|
app_icon: WEBP, PNG, GIF ці JPG. Заменіце прадвызначаны значок праграмы на мабільных прыладах карыстальніцкім значком.
|
||||||
backups_retention_period: Карыстальнікі могуць ствараць архівы сваіх допісаў для наступнай запампоўкі. Пры станоўчай колькасці дзён гэтыя архівы будуць аўтаматычна выдаляцца са сховішча пасля заканчэння названай колькасці дзён.
|
backups_retention_period: Карыстальнікі могуць ствараць архівы сваіх допісаў для наступнай запампоўкі. Пры станоўчай колькасці дзён гэтыя архівы будуць аўтаматычна выдаляцца са сховішча пасля заканчэння названай колькасці дзён.
|
||||||
bootstrap_timeline_accounts: Гэтыя ўліковыя запісы будуць замацаваны ў топе рэкамендацый для новых карыстальнікаў.
|
bootstrap_timeline_accounts: Гэтыя ўліковыя запісы будуць прымацаваныя наверсе рэкамендацый для новых карыстальнікаў. Дайце спіс уліковых запісаў, выкарыстоўваючы коску, каб раздзяліць іх.
|
||||||
closed_registrations_message: Паказваецца, калі рэгістрацыя закрытая
|
closed_registrations_message: Паказваецца, калі рэгістрацыя закрытая
|
||||||
content_cache_retention_period: Усе допісы з іншых сервераў (разам з пашырэннямі і адказамі) будуць выдалены праз паказаную колькасць дзён, незалежна ад таго, як лакальны карыстальнік узаемадзейнічаў з гэтымі допісамі. Гэта датычыцца і тых допісаў, якія лакальны карыстальнік пазначыў у закладкі або ўпадабанае. Прыватныя згадванні паміж карыстальнікамі з розных экзэмпляраў сервераў таксама будуць страчаны і іх нельга будзе аднавіць. Выкарыстанне гэтай налады прызначана для экзэмпляраў сервераў спецыяльнага прызначэння і парушае многія чаканні карыстальнікаў пры выкарыстанні ў агульных мэтах.
|
content_cache_retention_period: Усе допісы з іншых сервераў (разам з пашырэннямі і адказамі) будуць выдалены праз паказаную колькасць дзён, незалежна ад таго, як лакальны карыстальнік узаемадзейнічаў з гэтымі допісамі. Гэта датычыцца і тых допісаў, якія лакальны карыстальнік пазначыў у закладкі або ўпадабанае. Прыватныя згадванні паміж карыстальнікамі з розных экзэмпляраў сервераў таксама будуць страчаны і іх нельга будзе аднавіць. Выкарыстанне гэтай налады прызначана для экзэмпляраў сервераў спецыяльнага прызначэння і парушае многія чаканні карыстальнікаў пры выкарыстанні ў агульных мэтах.
|
||||||
custom_css: Вы можаце прымяняць карыстальніцкія стылі ў вэб-версіі Mastodon.
|
custom_css: Вы можаце прымяняць карыстальніцкія стылі ў вэб-версіі Mastodon.
|
||||||
|
|||||||
@ -82,7 +82,6 @@ bg:
|
|||||||
activity_api_enabled: Броят на местните публикувани публикации, дейни потребители и нови регистрации в седмични кофи
|
activity_api_enabled: Броят на местните публикувани публикации, дейни потребители и нови регистрации в седмични кофи
|
||||||
app_icon: WEBP, PNG, GIF или JPG. Заменя подразбиращата се икона на приложението в мобилни устройства с произволна икона.
|
app_icon: WEBP, PNG, GIF или JPG. Заменя подразбиращата се икона на приложението в мобилни устройства с произволна икона.
|
||||||
backups_retention_period: Потребителите имат способността да пораждат архиви от публикациите си за по-късно изтегляне. Задавайки положителна стойност, тези архиви самодейно ще се изтрият от хранилището ви след определения брой дни.
|
backups_retention_period: Потребителите имат способността да пораждат архиви от публикациите си за по-късно изтегляне. Задавайки положителна стойност, тези архиви самодейно ще се изтрият от хранилището ви след определения брой дни.
|
||||||
bootstrap_timeline_accounts: Тези акаунти ще се закачат в горния край на препоръките за следване на нови потребители.
|
|
||||||
closed_registrations_message: Показва се, когато е затворено за регистрации
|
closed_registrations_message: Показва се, когато е затворено за регистрации
|
||||||
content_cache_retention_period: Всички публикации от други сървъри, включително подсилвания и отговори, ще се изтрият след посочения брой дни, без да се взема предвид каквото и да е взаимодействие на местния потребител с тези публикации. Това включва публикации, които местния потребител е означил като отметки или любими. Личните споменавания между потребители от различни инстанции също ще се загубят и невъзможно да се възстановят. Употребата на тази настройка е предназначена за случаи със специално предназначение и разбива очакванията на много потребители, когато се изпълнява за употреба с общо предназначение.
|
content_cache_retention_period: Всички публикации от други сървъри, включително подсилвания и отговори, ще се изтрият след посочения брой дни, без да се взема предвид каквото и да е взаимодействие на местния потребител с тези публикации. Това включва публикации, които местния потребител е означил като отметки или любими. Личните споменавания между потребители от различни инстанции също ще се загубят и невъзможно да се възстановят. Употребата на тази настройка е предназначена за случаи със специално предназначение и разбива очакванията на много потребители, когато се изпълнява за употреба с общо предназначение.
|
||||||
custom_css: Може да прилагате собствени стилове в уебверсията на Mastodon.
|
custom_css: Може да прилагате собствени стилове в уебверсията на Mastodon.
|
||||||
|
|||||||
@ -85,7 +85,6 @@ ca:
|
|||||||
activity_api_enabled: Contador de tuts publicats localment, usuaris actius i registres nous en períodes setmanals
|
activity_api_enabled: Contador de tuts publicats localment, usuaris actius i registres nous en períodes setmanals
|
||||||
app_icon: WEBP, PNG, GIF o JPG. Canvia la icona per defecte de l'app en dispositius mòbils per una de personalitzada.
|
app_icon: WEBP, PNG, GIF o JPG. Canvia la icona per defecte de l'app en dispositius mòbils per una de personalitzada.
|
||||||
backups_retention_period: Els usuaris poden generar arxius de les seves publicacions per a baixar-los més endavant. Quan tingui un valor positiu, els arxius s'esborraran del vostre emmagatzematge després del nombre donat de dies.
|
backups_retention_period: Els usuaris poden generar arxius de les seves publicacions per a baixar-los més endavant. Quan tingui un valor positiu, els arxius s'esborraran del vostre emmagatzematge després del nombre donat de dies.
|
||||||
bootstrap_timeline_accounts: Aquests comptes es fixaran en la part superior de les recomanacions de seguiment dels nous usuaris.
|
|
||||||
closed_registrations_message: Es mostra quan el registres estan tancats
|
closed_registrations_message: Es mostra quan el registres estan tancats
|
||||||
content_cache_retention_period: S'esborraran totes les publicacions d'altres servidors (impulsos i respostes inclosos) passats els dies indicats, sense tenir en consideració les interaccions d'usuaris locals amb aquestes publicacions. Això inclou les publicacions que un usuari local hagi marcat com a favorites. També es perdran, i no es podran recuperar, les mencions privades entre usuaris d'instàncies diferents. Aquest paràmetre està pensat per a instàncies amb un propòsit especial i trencarà les expectatives dels usuaris si s'utilitza en una instància convencional.
|
content_cache_retention_period: S'esborraran totes les publicacions d'altres servidors (impulsos i respostes inclosos) passats els dies indicats, sense tenir en consideració les interaccions d'usuaris locals amb aquestes publicacions. Això inclou les publicacions que un usuari local hagi marcat com a favorites. També es perdran, i no es podran recuperar, les mencions privades entre usuaris d'instàncies diferents. Aquest paràmetre està pensat per a instàncies amb un propòsit especial i trencarà les expectatives dels usuaris si s'utilitza en una instància convencional.
|
||||||
custom_css: Pots aplicar estils personalitzats en la versió web de Mastodon.
|
custom_css: Pots aplicar estils personalitzats en la versió web de Mastodon.
|
||||||
|
|||||||
@ -88,7 +88,7 @@ cs:
|
|||||||
activity_api_enabled: Počty lokálně zveřejnělých příspěvků, aktivních uživatelů a nových registrací v týdenních intervalech
|
activity_api_enabled: Počty lokálně zveřejnělých příspěvků, aktivních uživatelů a nových registrací v týdenních intervalech
|
||||||
app_icon: WEBP, PNG, GIF nebo JPG. Nahradí výchozí ikonu aplikace v mobilních zařízeních vlastní ikonou.
|
app_icon: WEBP, PNG, GIF nebo JPG. Nahradí výchozí ikonu aplikace v mobilních zařízeních vlastní ikonou.
|
||||||
backups_retention_period: Uživatelé mají možnost vytvářet archivy svých příspěvků, které si mohou stáhnout později. Pokud je nastaveno na kladnou hodnotu, budou tyto archivy po zadaném počtu dní automaticky odstraněny z úložiště.
|
backups_retention_period: Uživatelé mají možnost vytvářet archivy svých příspěvků, které si mohou stáhnout později. Pokud je nastaveno na kladnou hodnotu, budou tyto archivy po zadaném počtu dní automaticky odstraněny z úložiště.
|
||||||
bootstrap_timeline_accounts: Tyto účty budou připnuty na vrchol nových uživatelů podle doporučení.
|
bootstrap_timeline_accounts: Tyto účty budou připnuty na vrcholu doporučení pro nové uživatele. Napište čárkou oddělený seznam účtů.
|
||||||
closed_registrations_message: Zobrazeno při zavření registrace
|
closed_registrations_message: Zobrazeno při zavření registrace
|
||||||
content_cache_retention_period: Všechny příspěvky z jiných serverů (včetně boostů a odpovědí) budou po uplynutí stanoveného počtu dní smazány bez ohledu na interakci místního uživatele s těmito příspěvky. To se týká i příspěvků, které místní uživatel přidal do záložek nebo oblíbených. Soukromé zmínky mezi uživateli z různých instancí budou rovněž ztraceny a nebude možné je obnovit. Použití tohoto nastavení je určeno pro instance pro speciální účely a při implementaci pro obecné použití porušuje mnohá očekávání uživatelů.
|
content_cache_retention_period: Všechny příspěvky z jiných serverů (včetně boostů a odpovědí) budou po uplynutí stanoveného počtu dní smazány bez ohledu na interakci místního uživatele s těmito příspěvky. To se týká i příspěvků, které místní uživatel přidal do záložek nebo oblíbených. Soukromé zmínky mezi uživateli z různých instancí budou rovněž ztraceny a nebude možné je obnovit. Použití tohoto nastavení je určeno pro instance pro speciální účely a při implementaci pro obecné použití porušuje mnohá očekávání uživatelů.
|
||||||
custom_css: Můžete použít vlastní styly ve verzi Mastodonu.
|
custom_css: Můžete použít vlastní styly ve verzi Mastodonu.
|
||||||
|
|||||||
@ -79,6 +79,7 @@ cy:
|
|||||||
featured_tag:
|
featured_tag:
|
||||||
name: 'Dyma rai o’r hashnodau a ddefnyddioch chi''n ddiweddar:'
|
name: 'Dyma rai o’r hashnodau a ddefnyddioch chi''n ddiweddar:'
|
||||||
filters:
|
filters:
|
||||||
|
action: Dewiswch pa weithred i'w gyflawni pan fydd postiad yn cyd-fynd â'r hidlydd
|
||||||
actions:
|
actions:
|
||||||
blur: Cuddio cyfryngau tu ôl i rybudd, heb guddio'r testun ei hun
|
blur: Cuddio cyfryngau tu ôl i rybudd, heb guddio'r testun ei hun
|
||||||
hide: Cuddiwch y cynnwys wedi'i hidlo'n llwyr, gan ymddwyn fel pe na bai'n bodoli
|
hide: Cuddiwch y cynnwys wedi'i hidlo'n llwyr, gan ymddwyn fel pe na bai'n bodoli
|
||||||
@ -87,7 +88,7 @@ cy:
|
|||||||
activity_api_enabled: Cyfrif o bostiadau a gyhoeddir yn lleol, defnyddwyr gweithredol, a chofrestriadau newydd mewn bwcedi wythnosol
|
activity_api_enabled: Cyfrif o bostiadau a gyhoeddir yn lleol, defnyddwyr gweithredol, a chofrestriadau newydd mewn bwcedi wythnosol
|
||||||
app_icon: WEBP, PNG, GIF neu JPG. Yn diystyru'r eicon ap rhagosodedig ar ddyfeisiau symudol gydag eicon cyfaddas.
|
app_icon: WEBP, PNG, GIF neu JPG. Yn diystyru'r eicon ap rhagosodedig ar ddyfeisiau symudol gydag eicon cyfaddas.
|
||||||
backups_retention_period: Mae gan ddefnyddwyr y gallu i gynhyrchu archifau o'u postiadau i'w llwytho i lawr yn ddiweddarach. Pan gânt eu gosod i werth positif, bydd yr archifau hyn yn cael eu dileu'n awtomatig o'ch storfa ar ôl y nifer penodedig o ddyddiau.
|
backups_retention_period: Mae gan ddefnyddwyr y gallu i gynhyrchu archifau o'u postiadau i'w llwytho i lawr yn ddiweddarach. Pan gânt eu gosod i werth positif, bydd yr archifau hyn yn cael eu dileu'n awtomatig o'ch storfa ar ôl y nifer penodedig o ddyddiau.
|
||||||
bootstrap_timeline_accounts: Bydd y cyfrifon hyn yn cael eu pinio i frig argymhellion dilynol defnyddwyr newydd.
|
bootstrap_timeline_accounts: Bydd y cyfrifon yma'n cael eu pinio i frig argymhellion dilyn defnyddwyr newydd. Darparwch restr cyfrifon wedi'u gwahanu gan gollnod.
|
||||||
closed_registrations_message: Yn cael eu dangos pan fydd cofrestriadau wedi cau
|
closed_registrations_message: Yn cael eu dangos pan fydd cofrestriadau wedi cau
|
||||||
content_cache_retention_period: Bydd yr holl bostiadau gan weinyddion eraill (gan gynnwys hwb ac atebion) yn cael eu dileu ar ôl y nifer penodedig o ddyddiau, heb ystyried unrhyw ryngweithio defnyddiwr lleol â'r postiadau hynny. Mae hyn yn cynnwys postiadau lle mae defnyddiwr lleol wedi ei farcio fel nodau tudalen neu ffefrynnau. Bydd cyfeiriadau preifat rhwng defnyddwyr o wahanol achosion hefyd yn cael eu colli ac yn amhosibl eu hadfer. Mae'r defnydd o'r gosodiad hwn wedi'i fwriadu ar gyfer achosion pwrpas arbennig ac mae'n torri llawer o ddisgwyliadau defnyddwyr pan gaiff ei weithredu at ddibenion cyffredinol.
|
content_cache_retention_period: Bydd yr holl bostiadau gan weinyddion eraill (gan gynnwys hwb ac atebion) yn cael eu dileu ar ôl y nifer penodedig o ddyddiau, heb ystyried unrhyw ryngweithio defnyddiwr lleol â'r postiadau hynny. Mae hyn yn cynnwys postiadau lle mae defnyddiwr lleol wedi ei farcio fel nodau tudalen neu ffefrynnau. Bydd cyfeiriadau preifat rhwng defnyddwyr o wahanol achosion hefyd yn cael eu colli ac yn amhosibl eu hadfer. Mae'r defnydd o'r gosodiad hwn wedi'i fwriadu ar gyfer achosion pwrpas arbennig ac mae'n torri llawer o ddisgwyliadau defnyddwyr pan gaiff ei weithredu at ddibenion cyffredinol.
|
||||||
custom_css: Gallwch gymhwyso arddulliau cyfaddas ar fersiwn gwe Mastodon.
|
custom_css: Gallwch gymhwyso arddulliau cyfaddas ar fersiwn gwe Mastodon.
|
||||||
|
|||||||
@ -88,7 +88,7 @@ da:
|
|||||||
activity_api_enabled: Antal lokalt opslåede indlæg, aktive brugere samt nye tilmeldinger i ugentlige opdelinger
|
activity_api_enabled: Antal lokalt opslåede indlæg, aktive brugere samt nye tilmeldinger i ugentlige opdelinger
|
||||||
app_icon: WEBP, PNG, GIF eller JPG. Tilsidesætter standard app-ikonet på mobilenheder med et tilpasset ikon.
|
app_icon: WEBP, PNG, GIF eller JPG. Tilsidesætter standard app-ikonet på mobilenheder med et tilpasset ikon.
|
||||||
backups_retention_period: Brugere har mulighed for at generere arkiver af deres indlæg til senere downloade. Når sat til positiv værdi, vil disse arkiver automatisk blive slettet fra lagerpladsen efter det angivne antal dage.
|
backups_retention_period: Brugere har mulighed for at generere arkiver af deres indlæg til senere downloade. Når sat til positiv værdi, vil disse arkiver automatisk blive slettet fra lagerpladsen efter det angivne antal dage.
|
||||||
bootstrap_timeline_accounts: Disse konti fastgøres øverst på nye brugeres følg-anbefalinger.
|
bootstrap_timeline_accounts: Disse konti vil blive fastgjort til toppen af nye brugeres følg-anbefalinger. Angiv en kommasepareret liste over konti.
|
||||||
closed_registrations_message: Vises, når tilmeldinger er lukket
|
closed_registrations_message: Vises, når tilmeldinger er lukket
|
||||||
content_cache_retention_period: Alle indlæg fra andre servere (herunder fremhævelser og besvarelser) slettes efter det angivne antal dage uden hensyn til lokal brugerinteraktion med disse indlæg. Dette omfatter indlæg, hvor en lokal bruger har markeret dem som bogmærker eller favoritter. Private omtaler mellem brugere fra forskellige instanser vil også være tabt og umulige at gendanne. Brugen af denne indstilling er beregnet til særlige formål instanser og bryder mange brugerforventninger ved implementering til almindelig brug.
|
content_cache_retention_period: Alle indlæg fra andre servere (herunder fremhævelser og besvarelser) slettes efter det angivne antal dage uden hensyn til lokal brugerinteraktion med disse indlæg. Dette omfatter indlæg, hvor en lokal bruger har markeret dem som bogmærker eller favoritter. Private omtaler mellem brugere fra forskellige instanser vil også være tabt og umulige at gendanne. Brugen af denne indstilling er beregnet til særlige formål instanser og bryder mange brugerforventninger ved implementering til almindelig brug.
|
||||||
custom_css: Man kan anvende tilpassede stilarter på Mastodon-webversionen.
|
custom_css: Man kan anvende tilpassede stilarter på Mastodon-webversionen.
|
||||||
|
|||||||
@ -88,7 +88,7 @@ de:
|
|||||||
activity_api_enabled: Anzahl der wöchentlichen Beiträge, aktiven Profile und Registrierungen auf diesem Server
|
activity_api_enabled: Anzahl der wöchentlichen Beiträge, aktiven Profile und Registrierungen auf diesem Server
|
||||||
app_icon: WEBP, PNG, GIF oder JPG. Überschreibt das Standard-App-Symbol auf mobilen Geräten mit einem eigenen Symbol.
|
app_icon: WEBP, PNG, GIF oder JPG. Überschreibt das Standard-App-Symbol auf mobilen Geräten mit einem eigenen Symbol.
|
||||||
backups_retention_period: Nutzer*innen haben die Möglichkeit, Archive ihrer Beiträge zu erstellen, die sie später herunterladen können. Wenn ein positiver Wert gesetzt ist, werden diese Archive nach der festgelegten Anzahl von Tagen automatisch aus deinem Speicher gelöscht.
|
backups_retention_period: Nutzer*innen haben die Möglichkeit, Archive ihrer Beiträge zu erstellen, die sie später herunterladen können. Wenn ein positiver Wert gesetzt ist, werden diese Archive nach der festgelegten Anzahl von Tagen automatisch aus deinem Speicher gelöscht.
|
||||||
bootstrap_timeline_accounts: Diese Konten werden bei den Follower-Empfehlungen für neu registrierte Nutzer*innen oben angeheftet.
|
bootstrap_timeline_accounts: Diese Konten werden an den Anfang der Follow-Empfehlungen für neue Nutzer angeheftet. Gib eine durch Kommata getrennte Liste von Konten an.
|
||||||
closed_registrations_message: Wird angezeigt, wenn Registrierungen deaktiviert sind
|
closed_registrations_message: Wird angezeigt, wenn Registrierungen deaktiviert sind
|
||||||
content_cache_retention_period: Sämtliche Beiträge von anderen Servern (einschließlich geteilte Beiträge und Antworten) werden, unabhängig von der Interaktion der lokalen Nutzer*innen mit diesen Beiträgen, nach der festgelegten Anzahl von Tagen gelöscht. Das betrifft auch Beiträge, die von lokalen Nutzer*innen favorisiert oder als Lesezeichen gespeichert wurden. Private Erwähnungen zwischen Nutzer*innen von verschiedenen Servern werden ebenfalls verloren gehen und können nicht wiederhergestellt werden. Diese Option richtet sich ausschließlich an Server mit speziellen Zwecken und wird die allgemeine Nutzungserfahrung beeinträchtigen, wenn sie für den allgemeinen Gebrauch aktiviert ist.
|
content_cache_retention_period: Sämtliche Beiträge von anderen Servern (einschließlich geteilte Beiträge und Antworten) werden, unabhängig von der Interaktion der lokalen Nutzer*innen mit diesen Beiträgen, nach der festgelegten Anzahl von Tagen gelöscht. Das betrifft auch Beiträge, die von lokalen Nutzer*innen favorisiert oder als Lesezeichen gespeichert wurden. Private Erwähnungen zwischen Nutzer*innen von verschiedenen Servern werden ebenfalls verloren gehen und können nicht wiederhergestellt werden. Diese Option richtet sich ausschließlich an Server mit speziellen Zwecken und wird die allgemeine Nutzungserfahrung beeinträchtigen, wenn sie für den allgemeinen Gebrauch aktiviert ist.
|
||||||
custom_css: Du kannst benutzerdefinierte Stile auf die Web-Version von Mastodon anwenden.
|
custom_css: Du kannst benutzerdefinierte Stile auf die Web-Version von Mastodon anwenden.
|
||||||
|
|||||||
@ -88,7 +88,7 @@ el:
|
|||||||
activity_api_enabled: Καταμέτρηση τοπικά δημοσιευμένων δημοσιεύσεων, ενεργών χρηστών και νέων εγγραφών σε εβδομαδιαία πακέτα
|
activity_api_enabled: Καταμέτρηση τοπικά δημοσιευμένων δημοσιεύσεων, ενεργών χρηστών και νέων εγγραφών σε εβδομαδιαία πακέτα
|
||||||
app_icon: WEBP, PNG, GIF ή JPG. Παρακάμπτει το προεπιλεγμένο εικονίδιο εφαρμογής σε κινητές συσκευές με προσαρμοσμένο εικονίδιο.
|
app_icon: WEBP, PNG, GIF ή JPG. Παρακάμπτει το προεπιλεγμένο εικονίδιο εφαρμογής σε κινητές συσκευές με προσαρμοσμένο εικονίδιο.
|
||||||
backups_retention_period: Οι χρήστες έχουν τη δυνατότητα να δημιουργήσουν αρχεία των αναρτήσεων τους για να κατεβάσουν αργότερα. Όταν οριστεί μια θετική τιμή, αυτά τα αρχεία θα διαγράφονται αυτόματα από τον αποθηκευτικό σου χώρο μετά τον καθορισμένο αριθμό ημερών.
|
backups_retention_period: Οι χρήστες έχουν τη δυνατότητα να δημιουργήσουν αρχεία των αναρτήσεων τους για να κατεβάσουν αργότερα. Όταν οριστεί μια θετική τιμή, αυτά τα αρχεία θα διαγράφονται αυτόματα από τον αποθηκευτικό σου χώρο μετά τον καθορισμένο αριθμό ημερών.
|
||||||
bootstrap_timeline_accounts: Αυτοί οι λογαριασμοί θα καρφιτσωθούν στην κορυφή των νέων χρηστών που ακολουθούν τις συστάσεις.
|
bootstrap_timeline_accounts: Αυτοί οι λογαριασμοί θα καρφιτσωθούν στην κορυφή των προτεινόμενων ακολουθήσεων για νέους χρήστες. Παρέχετε μια λίστα λογαριασμών χωρισμένη με κόμμα.
|
||||||
closed_registrations_message: Εμφανίζεται όταν κλείνουν οι εγγραφές
|
closed_registrations_message: Εμφανίζεται όταν κλείνουν οι εγγραφές
|
||||||
content_cache_retention_period: Όλες οι αναρτήσεις από άλλους διακομιστές (συμπεριλαμβανομένων των ενισχύσεων και απαντήσεων) θα διαγραφούν μετά τον καθορισμένο αριθμό ημερών, χωρίς να λαμβάνεται υπόψη οποιαδήποτε αλληλεπίδραση τοπικού χρήστη με αυτές τις αναρτήσεις. Αυτό περιλαμβάνει αναρτήσεις όπου ένας τοπικός χρήστης την έχει χαρακτηρίσει ως σελιδοδείκτη ή αγαπημένη. Θα χαθούν επίσης ιδιωτικές αναφορές μεταξύ χρηστών από διαφορετικές οντότητες και θα είναι αδύνατο να αποκατασταθούν. Η χρήση αυτής της ρύθμισης προορίζεται για οντότητες ειδικού σκοπού και χαλάει πολλές προσδοκίες του χρήστη όταν εφαρμόζεται για χρήση γενική σκοπού.
|
content_cache_retention_period: Όλες οι αναρτήσεις από άλλους διακομιστές (συμπεριλαμβανομένων των ενισχύσεων και απαντήσεων) θα διαγραφούν μετά τον καθορισμένο αριθμό ημερών, χωρίς να λαμβάνεται υπόψη οποιαδήποτε αλληλεπίδραση τοπικού χρήστη με αυτές τις αναρτήσεις. Αυτό περιλαμβάνει αναρτήσεις όπου ένας τοπικός χρήστης την έχει χαρακτηρίσει ως σελιδοδείκτη ή αγαπημένη. Θα χαθούν επίσης ιδιωτικές αναφορές μεταξύ χρηστών από διαφορετικές οντότητες και θα είναι αδύνατο να αποκατασταθούν. Η χρήση αυτής της ρύθμισης προορίζεται για οντότητες ειδικού σκοπού και χαλάει πολλές προσδοκίες του χρήστη όταν εφαρμόζεται για χρήση γενική σκοπού.
|
||||||
custom_css: Μπορείς να εφαρμόσεις προσαρμοσμένα στυλ στην έκδοση ιστοσελίδας του Mastodon.
|
custom_css: Μπορείς να εφαρμόσεις προσαρμοσμένα στυλ στην έκδοση ιστοσελίδας του Mastodon.
|
||||||
|
|||||||
@ -82,7 +82,6 @@ en-GB:
|
|||||||
activity_api_enabled: Counts of locally published posts, active users, and new registrations in weekly buckets
|
activity_api_enabled: Counts of locally published posts, active users, and new registrations in weekly buckets
|
||||||
app_icon: WEBP, PNG, GIF or JPG. Overrides the default app icon on mobile devices with a custom icon.
|
app_icon: WEBP, PNG, GIF or JPG. Overrides the default app icon on mobile devices with a custom icon.
|
||||||
backups_retention_period: Users have the ability to generate archives of their posts to download later. When set to a positive value, these archives will be automatically deleted from your storage after the specified number of days.
|
backups_retention_period: Users have the ability to generate archives of their posts to download later. When set to a positive value, these archives will be automatically deleted from your storage after the specified number of days.
|
||||||
bootstrap_timeline_accounts: These accounts will be pinned to the top of new users' follow recommendations.
|
|
||||||
closed_registrations_message: Displayed when sign-ups are closed
|
closed_registrations_message: Displayed when sign-ups are closed
|
||||||
content_cache_retention_period: All posts from other servers (including boosts and replies) will be deleted after the specified number of days, without regard to any local user interaction with those posts. This includes posts where a local user has marked it as bookmarks or favorites. Private mentions between users from different instances will also be lost and impossible to restore. Use of this setting is intended for special purpose instances and breaks many user expectations when implemented for general purpose use.
|
content_cache_retention_period: All posts from other servers (including boosts and replies) will be deleted after the specified number of days, without regard to any local user interaction with those posts. This includes posts where a local user has marked it as bookmarks or favorites. Private mentions between users from different instances will also be lost and impossible to restore. Use of this setting is intended for special purpose instances and breaks many user expectations when implemented for general purpose use.
|
||||||
custom_css: You can apply custom styles on the web version of Mastodon.
|
custom_css: You can apply custom styles on the web version of Mastodon.
|
||||||
|
|||||||
@ -84,7 +84,6 @@ eo:
|
|||||||
activity_api_enabled: Nombroj de loke publikigitaj afiŝoj, aktivaj uzantoj kaj novaj registradoj en semajnaj siteloj
|
activity_api_enabled: Nombroj de loke publikigitaj afiŝoj, aktivaj uzantoj kaj novaj registradoj en semajnaj siteloj
|
||||||
app_icon: WEBP, PNG, GIF aŭ JPG. Anstataŭigas la defaŭltan aplikaĵan bildsimbolon sur porteblaj aparatoj kun propra bildsimbolo.
|
app_icon: WEBP, PNG, GIF aŭ JPG. Anstataŭigas la defaŭltan aplikaĵan bildsimbolon sur porteblaj aparatoj kun propra bildsimbolo.
|
||||||
backups_retention_period: Uzantoj havas la kapablon generi arkivojn de siaj afiŝoj por elŝuti poste. Kiam estas agordita al pozitiva valoro, ĉi tiuj arkivoj estos aŭtomate forigitaj de via stokado post la specifita nombro da tagoj.
|
backups_retention_period: Uzantoj havas la kapablon generi arkivojn de siaj afiŝoj por elŝuti poste. Kiam estas agordita al pozitiva valoro, ĉi tiuj arkivoj estos aŭtomate forigitaj de via stokado post la specifita nombro da tagoj.
|
||||||
bootstrap_timeline_accounts: Ĉi tiuj kontoj pinglitas al la supro de sekvorekomendoj de novaj uzantoj.
|
|
||||||
closed_registrations_message: Montrita kiam registroj fermitas
|
closed_registrations_message: Montrita kiam registroj fermitas
|
||||||
content_cache_retention_period: Ĉiuj afiŝoj de aliaj serviloj (inkluzive de diskonigoj kaj respondoj) estos forigitaj post la specifita nombro da tagoj, sen konsidero al iu ajn loka uzantinterago kun tiuj afiŝoj. Ĉi tio inkluzivas afiŝojn, kie loka uzanto markis ĝin kiel legosignojn aŭ ŝatatajn. Privataj mencioj inter uzantoj de malsamaj nodoj ankaŭ estos perditaj kaj neeble restaŭreblaj. Uzo de ĉi tiu agordo estas celita por specialcelaj okazoj kaj rompas multajn uzantajn atendojn kiam efektivigita por ĝenerala uzo.
|
content_cache_retention_period: Ĉiuj afiŝoj de aliaj serviloj (inkluzive de diskonigoj kaj respondoj) estos forigitaj post la specifita nombro da tagoj, sen konsidero al iu ajn loka uzantinterago kun tiuj afiŝoj. Ĉi tio inkluzivas afiŝojn, kie loka uzanto markis ĝin kiel legosignojn aŭ ŝatatajn. Privataj mencioj inter uzantoj de malsamaj nodoj ankaŭ estos perditaj kaj neeble restaŭreblaj. Uzo de ĉi tiu agordo estas celita por specialcelaj okazoj kaj rompas multajn uzantajn atendojn kiam efektivigita por ĝenerala uzo.
|
||||||
custom_css: Vi povas meti propajn stilojn en la retversio de Mastodon.
|
custom_css: Vi povas meti propajn stilojn en la retversio de Mastodon.
|
||||||
|
|||||||
@ -88,7 +88,7 @@ es-AR:
|
|||||||
activity_api_enabled: Conteos de mensajes publicados localmente, cuentas activas y nuevos registros en tandas semanales
|
activity_api_enabled: Conteos de mensajes publicados localmente, cuentas activas y nuevos registros en tandas semanales
|
||||||
app_icon: WEBP, PNG, GIF o JPG. Reemplaza el ícono de aplicación predeterminado en dispositivos móviles con uno personalizado.
|
app_icon: WEBP, PNG, GIF o JPG. Reemplaza el ícono de aplicación predeterminado en dispositivos móviles con uno personalizado.
|
||||||
backups_retention_period: Los usuarios tienen la capacidad de generar archivos historiales de sus mensajes para descargar más adelante. Cuando se establece un valor positivo, estos archivos se eliminarán automáticamente de su almacenamiento después del número especificado de días.
|
backups_retention_period: Los usuarios tienen la capacidad de generar archivos historiales de sus mensajes para descargar más adelante. Cuando se establece un valor positivo, estos archivos se eliminarán automáticamente de su almacenamiento después del número especificado de días.
|
||||||
bootstrap_timeline_accounts: Estas cuentas serán fijadas a la parte superior de las recomendaciones de cuentas a seguir para nuevos usuarios.
|
bootstrap_timeline_accounts: Estas cuentas se fijarán en la parte superior de las recomendaciones de seguimiento para los nuevos usuarios. Proporcioná una lista de cuentas separadas por comas.
|
||||||
closed_registrations_message: Mostrado cuando los registros están cerrados
|
closed_registrations_message: Mostrado cuando los registros están cerrados
|
||||||
content_cache_retention_period: Todos los mensajes de otros servidores (incluyendo adhesiones y respuestas) se eliminarán después del número de días especificado, sin tener en cuenta la interacción del usuario local con esos mensajes. Esto incluye mensajes que un usuario local haya agregado a marcadores o los haya marcado como favoritos. Las menciones privadas entre usuarios de diferentes servidores también se perderán y también serán imposibles de restaurar. El uso de esta configuración está destinado a servidores de propósito especial y rompe muchas expectativas de los usuarios cuando se implementa para uso general.
|
content_cache_retention_period: Todos los mensajes de otros servidores (incluyendo adhesiones y respuestas) se eliminarán después del número de días especificado, sin tener en cuenta la interacción del usuario local con esos mensajes. Esto incluye mensajes que un usuario local haya agregado a marcadores o los haya marcado como favoritos. Las menciones privadas entre usuarios de diferentes servidores también se perderán y también serán imposibles de restaurar. El uso de esta configuración está destinado a servidores de propósito especial y rompe muchas expectativas de los usuarios cuando se implementa para uso general.
|
||||||
custom_css: Podés aplicar estilos personalizados a la versión web de Mastodon.
|
custom_css: Podés aplicar estilos personalizados a la versión web de Mastodon.
|
||||||
|
|||||||
@ -88,7 +88,7 @@ es-MX:
|
|||||||
activity_api_enabled: Conteo de publicaciones publicadas localmente, usuarios activos, y nuevos registros en periodos semanales
|
activity_api_enabled: Conteo de publicaciones publicadas localmente, usuarios activos, y nuevos registros en periodos semanales
|
||||||
app_icon: WEBP, PNG, GIF o JPG. Reemplaza el icono de aplicación predeterminado en dispositivos móviles con un icono personalizado.
|
app_icon: WEBP, PNG, GIF o JPG. Reemplaza el icono de aplicación predeterminado en dispositivos móviles con un icono personalizado.
|
||||||
backups_retention_period: Los usuarios tienen la posibilidad de generar archivos de sus mensajes para descargarlos más adelante. Cuando se establece en un valor positivo, estos archivos se eliminarán automáticamente del almacenamiento después del número especificado de días.
|
backups_retention_period: Los usuarios tienen la posibilidad de generar archivos de sus mensajes para descargarlos más adelante. Cuando se establece en un valor positivo, estos archivos se eliminarán automáticamente del almacenamiento después del número especificado de días.
|
||||||
bootstrap_timeline_accounts: Estas cuentas aparecerán en la parte superior de las recomendaciones de los nuevos usuarios.
|
bootstrap_timeline_accounts: Estas cuentas se colocarán en la parte superior de las recomendaciones de cuentas a las que seguir para los nuevos usuarios. Proporciona una lista de cuentas separadas por comas.
|
||||||
closed_registrations_message: Mostrado cuando los registros están cerrados
|
closed_registrations_message: Mostrado cuando los registros están cerrados
|
||||||
content_cache_retention_period: Todas las publicaciones de otros servidores (incluyendo impuestos y respuestas) serán borrados después del número de días especificado, sin tener en cuenta cualquier interacción del usuario local con esas publicaciones. Esto incluye los mensajes que un usuario local haya marcado como favoritos. Las menciones privadas entre usuarios de diferentes instancias también se perderán y será imposible restaurarlas. El uso de esta configuración está pensado para instancias de propósito especial y rompe muchas expectativas de los usuarios cuando se implementa para uso general.
|
content_cache_retention_period: Todas las publicaciones de otros servidores (incluyendo impuestos y respuestas) serán borrados después del número de días especificado, sin tener en cuenta cualquier interacción del usuario local con esas publicaciones. Esto incluye los mensajes que un usuario local haya marcado como favoritos. Las menciones privadas entre usuarios de diferentes instancias también se perderán y será imposible restaurarlas. El uso de esta configuración está pensado para instancias de propósito especial y rompe muchas expectativas de los usuarios cuando se implementa para uso general.
|
||||||
custom_css: Puedes aplicar estilos personalizados a la versión web de Mastodon.
|
custom_css: Puedes aplicar estilos personalizados a la versión web de Mastodon.
|
||||||
@ -242,7 +242,7 @@ es-MX:
|
|||||||
setting_default_privacy: Visibilidad de publicación
|
setting_default_privacy: Visibilidad de publicación
|
||||||
setting_default_quote_policy: Quién puede citar
|
setting_default_quote_policy: Quién puede citar
|
||||||
setting_default_sensitive: Marcar siempre imágenes como sensibles
|
setting_default_sensitive: Marcar siempre imágenes como sensibles
|
||||||
setting_delete_modal: Avisarme antes de borrar una publicación
|
setting_delete_modal: Avisarme antes de eliminar una publicación
|
||||||
setting_disable_hover_cards: Desactivar vista previa del perfil al pasar el cursor
|
setting_disable_hover_cards: Desactivar vista previa del perfil al pasar el cursor
|
||||||
setting_disable_swiping: Deshabilitar movimientos de deslizamiento
|
setting_disable_swiping: Deshabilitar movimientos de deslizamiento
|
||||||
setting_display_media: Visualización multimedia
|
setting_display_media: Visualización multimedia
|
||||||
@ -252,7 +252,7 @@ es-MX:
|
|||||||
setting_emoji_style: Estilo de emoji
|
setting_emoji_style: Estilo de emoji
|
||||||
setting_expand_spoilers: Siempre expandir las publicaciones marcadas con advertencias de contenido
|
setting_expand_spoilers: Siempre expandir las publicaciones marcadas con advertencias de contenido
|
||||||
setting_hide_network: Ocultar tu red
|
setting_hide_network: Ocultar tu red
|
||||||
setting_missing_alt_text_modal: Avisarme antes de publicar multimedia sin descripción de texto
|
setting_missing_alt_text_modal: Avisarme antes de publicar contenido multimedia sin descripción de texto
|
||||||
setting_quick_boosting: Habilitar impulso rápido
|
setting_quick_boosting: Habilitar impulso rápido
|
||||||
setting_reduce_motion: Reducir el movimiento de las animaciones
|
setting_reduce_motion: Reducir el movimiento de las animaciones
|
||||||
setting_system_font_ui: Usar la fuente por defecto del sistema
|
setting_system_font_ui: Usar la fuente por defecto del sistema
|
||||||
|
|||||||
@ -88,7 +88,7 @@ es:
|
|||||||
activity_api_enabled: Conteo de publicaciones publicadas localmente, usuarios activos y registros nuevos cada semana
|
activity_api_enabled: Conteo de publicaciones publicadas localmente, usuarios activos y registros nuevos cada semana
|
||||||
app_icon: WEBP, PNG, GIF o JPG. Reemplaza el icono de aplicación predeterminado en dispositivos móviles con un icono personalizado.
|
app_icon: WEBP, PNG, GIF o JPG. Reemplaza el icono de aplicación predeterminado en dispositivos móviles con un icono personalizado.
|
||||||
backups_retention_period: Los usuarios tienen la capacidad de generar archivos de sus mensajes para descargar más adelante. Cuando se establece un valor positivo, estos archivos se eliminarán automáticamente del almacenamiento después del número de días especificado.
|
backups_retention_period: Los usuarios tienen la capacidad de generar archivos de sus mensajes para descargar más adelante. Cuando se establece un valor positivo, estos archivos se eliminarán automáticamente del almacenamiento después del número de días especificado.
|
||||||
bootstrap_timeline_accounts: Estas cuentas aparecerán en la parte superior de las recomendaciones de los nuevos usuarios.
|
bootstrap_timeline_accounts: Estas cuentas se colocarán en la parte superior de las recomendaciones de cuentas a las que seguir para los nuevos usuarios. Proporciona una lista de cuentas separadas por comas.
|
||||||
closed_registrations_message: Mostrado cuando los registros están cerrados
|
closed_registrations_message: Mostrado cuando los registros están cerrados
|
||||||
content_cache_retention_period: Todas las publicaciones de otros servidores (incluso impulsos y respuestas) se eliminarán después del número de días especificado, sin tener en cuenta la interacción del usuario local con esos mensajes. Esto incluye mensajes donde un usuario local los ha marcado como marcadores o favoritos. Las menciones privadas entre usuarios de diferentes instancias también se perderán sin posibilidad de recuperación. El uso de esta configuración está destinado a instancias de propósito especial, y rompe muchas expectativas de los usuarios cuando se implementa para un uso de propósito general.
|
content_cache_retention_period: Todas las publicaciones de otros servidores (incluso impulsos y respuestas) se eliminarán después del número de días especificado, sin tener en cuenta la interacción del usuario local con esos mensajes. Esto incluye mensajes donde un usuario local los ha marcado como marcadores o favoritos. Las menciones privadas entre usuarios de diferentes instancias también se perderán sin posibilidad de recuperación. El uso de esta configuración está destinado a instancias de propósito especial, y rompe muchas expectativas de los usuarios cuando se implementa para un uso de propósito general.
|
||||||
custom_css: Puedes aplicar estilos personalizados a la versión web de Mastodon.
|
custom_css: Puedes aplicar estilos personalizados a la versión web de Mastodon.
|
||||||
|
|||||||
@ -88,7 +88,7 @@ et:
|
|||||||
activity_api_enabled: Kohalike postituste, aktiivsete kasutajate ja uute registreerumistr arv nädala kaupa grupeeritult
|
activity_api_enabled: Kohalike postituste, aktiivsete kasutajate ja uute registreerumistr arv nädala kaupa grupeeritult
|
||||||
app_icon: WEBP, PNG, GIF või JPG. Asendab mobiilsel seadmel äpi vaikeikooni kohandatud ikooniga.
|
app_icon: WEBP, PNG, GIF või JPG. Asendab mobiilsel seadmel äpi vaikeikooni kohandatud ikooniga.
|
||||||
backups_retention_period: Kasutajatel on võimalus genereerida oma postitustest hiljem allalaaditav arhiiv. Kui määrad positiivse arvu, siis kustutatakse need arhiivid serveri andmeruumist määratud arvu päevade järel automaatselt.
|
backups_retention_period: Kasutajatel on võimalus genereerida oma postitustest hiljem allalaaditav arhiiv. Kui määrad positiivse arvu, siis kustutatakse need arhiivid serveri andmeruumist määratud arvu päevade järel automaatselt.
|
||||||
bootstrap_timeline_accounts: Need kasutajad kinnitatakse uute kasutajate jälgimissoovituste esiritta.
|
bootstrap_timeline_accounts: Järgnevad kasutajakontod kinnitatakse vaate ülaossa ning on mõeldud jälgitavate kontode soovituseks uutele kasutajatele. Sisend peab olema komadega eraldatud kasutajakontode loend.
|
||||||
closed_registrations_message: Kuvatakse, kui liitumised pole võimalikud
|
closed_registrations_message: Kuvatakse, kui liitumised pole võimalikud
|
||||||
content_cache_retention_period: Kõik teiste serverite postitused (sealhulgas jagamised ja vastused) kustutatakse pärast määratud arvu päevade möödumist, sõltumata, kuidas kohalik kasutaja on nende postitustega interakteerunud. Hõlmatud on ka postitused, mille kohalik kasutaja on märkinud järjehoidjaks või lemmikuks. Ka eri instantside kasutajate vahelised privaatsed mainimised kaovad ja neid on võimatu taastada. See seadistus on mõeldud eriotstarbeliste instantside jaoks ja rikub paljude kasutajate ootusi, kui seda rakendatakse üldotstarbelise kasutuse puhul.
|
content_cache_retention_period: Kõik teiste serverite postitused (sealhulgas jagamised ja vastused) kustutatakse pärast määratud arvu päevade möödumist, sõltumata, kuidas kohalik kasutaja on nende postitustega interakteerunud. Hõlmatud on ka postitused, mille kohalik kasutaja on märkinud järjehoidjaks või lemmikuks. Ka eri instantside kasutajate vahelised privaatsed mainimised kaovad ja neid on võimatu taastada. See seadistus on mõeldud eriotstarbeliste instantside jaoks ja rikub paljude kasutajate ootusi, kui seda rakendatakse üldotstarbelise kasutuse puhul.
|
||||||
custom_css: Mastodoni veebiliideses on võimalik kasutada kohandatud stiile.
|
custom_css: Mastodoni veebiliideses on võimalik kasutada kohandatud stiile.
|
||||||
|
|||||||
@ -82,7 +82,6 @@ eu:
|
|||||||
activity_api_enabled: Lokalki argitaratutako bidalketak, erabiltzaile aktiboak, eta izen-emateen kopuruak astero zenbatzen ditu
|
activity_api_enabled: Lokalki argitaratutako bidalketak, erabiltzaile aktiboak, eta izen-emateen kopuruak astero zenbatzen ditu
|
||||||
app_icon: WEBP, PNG, GIF edo JPG. Aplikazioaren ikono lehenetsia gainidazten du ikono pertsonalizatu batekin gailu mugikorretan.
|
app_icon: WEBP, PNG, GIF edo JPG. Aplikazioaren ikono lehenetsia gainidazten du ikono pertsonalizatu batekin gailu mugikorretan.
|
||||||
backups_retention_period: Erabiltzaileek geroago deskarga dezaketen beren argitalpenen artxiboak sor ditzakete. Balio positibo bat ezartzean, artxibo hauek biltegiratzetik automatikoki ezabatuko dira zehazturiko egunen buruan.
|
backups_retention_period: Erabiltzaileek geroago deskarga dezaketen beren argitalpenen artxiboak sor ditzakete. Balio positibo bat ezartzean, artxibo hauek biltegiratzetik automatikoki ezabatuko dira zehazturiko egunen buruan.
|
||||||
bootstrap_timeline_accounts: Kontu hauek erabiltzaile berrien jarraitzeko gomendioen goiko aldean ainguratuko dira.
|
|
||||||
closed_registrations_message: Izen-ematea itxia dagoenean bistaratua
|
closed_registrations_message: Izen-ematea itxia dagoenean bistaratua
|
||||||
content_cache_retention_period: Beste zerbitzarietako argitalpen guztiak (bultzadak eta erantzunak barne) ezabatuko dira zehazturiko egunen buruan, argitalpen horiek izan ditzaketen erabiltzaile lokalaren interakzioa kontuan izanik gabe. Instantzia desberdinetako erabiltzaileen arteko aipamen pribatuak ere galdu egingo dira eta ezin izango dira berreskuratu. Ezarpen honen erabilera xede berezia duten instantziei zuzendua dago eta erabiltzaileen itxaropena hausten da orotariko erabilerarako inplementatzean.
|
content_cache_retention_period: Beste zerbitzarietako argitalpen guztiak (bultzadak eta erantzunak barne) ezabatuko dira zehazturiko egunen buruan, argitalpen horiek izan ditzaketen erabiltzaile lokalaren interakzioa kontuan izanik gabe. Instantzia desberdinetako erabiltzaileen arteko aipamen pribatuak ere galdu egingo dira eta ezin izango dira berreskuratu. Ezarpen honen erabilera xede berezia duten instantziei zuzendua dago eta erabiltzaileen itxaropena hausten da orotariko erabilerarako inplementatzean.
|
||||||
custom_css: Estilo pertsonalizatuak aplikatu ditzakezu Mastodonen web bertsioan.
|
custom_css: Estilo pertsonalizatuak aplikatu ditzakezu Mastodonen web bertsioan.
|
||||||
|
|||||||
@ -84,7 +84,6 @@ fa:
|
|||||||
activity_api_enabled: تعداد بوقهای منتشرهٔ محلی، کاربران فعال، و کاربران تازه در هر هفته
|
activity_api_enabled: تعداد بوقهای منتشرهٔ محلی، کاربران فعال، و کاربران تازه در هر هفته
|
||||||
app_icon: WEBP، PNG، GIF یا JPG. با یک نماد سفارشی، نماد برنامه پیشفرض را در دستگاههای تلفن همراه لغو میکند.
|
app_icon: WEBP، PNG، GIF یا JPG. با یک نماد سفارشی، نماد برنامه پیشفرض را در دستگاههای تلفن همراه لغو میکند.
|
||||||
backups_retention_period: کاربران می توانند بایگانی هایی از پست های خود ایجاد کنند تا بعدا دانلود کنند. وقتی روی مقدار مثبت تنظیم شود، این بایگانیها پس از تعداد روزهای مشخص شده بهطور خودکار از فضای ذخیرهسازی شما حذف میشوند.
|
backups_retention_period: کاربران می توانند بایگانی هایی از پست های خود ایجاد کنند تا بعدا دانلود کنند. وقتی روی مقدار مثبت تنظیم شود، این بایگانیها پس از تعداد روزهای مشخص شده بهطور خودکار از فضای ذخیرهسازی شما حذف میشوند.
|
||||||
bootstrap_timeline_accounts: سنجاق کردنThese accounts will be pinned to the top of new users' follow recommendations.
|
|
||||||
closed_registrations_message: نمایش داده هنگام بسته بودن ثبتنامها
|
closed_registrations_message: نمایش داده هنگام بسته بودن ثبتنامها
|
||||||
content_cache_retention_period: همه پستهای سرورهای دیگر (از جمله تقویتکنندهها و پاسخها) پس از چند روز مشخص شده، بدون توجه به هرگونه تعامل کاربر محلی با آن پستها، حذف خواهند شد. این شامل پست هایی می شود که یک کاربر محلی آن را به عنوان نشانک یا موارد دلخواه علامت گذاری کرده است. ذکر خصوصی بین کاربران از نمونه های مختلف نیز از بین خواهد رفت و بازیابی آنها غیرممکن است. استفاده از این تنظیم برای موارد با هدف خاص در نظر گرفته شده است و بسیاری از انتظارات کاربر را هنگامی که برای استفاده عمومی اجرا می شود، از بین می برد.
|
content_cache_retention_period: همه پستهای سرورهای دیگر (از جمله تقویتکنندهها و پاسخها) پس از چند روز مشخص شده، بدون توجه به هرگونه تعامل کاربر محلی با آن پستها، حذف خواهند شد. این شامل پست هایی می شود که یک کاربر محلی آن را به عنوان نشانک یا موارد دلخواه علامت گذاری کرده است. ذکر خصوصی بین کاربران از نمونه های مختلف نیز از بین خواهد رفت و بازیابی آنها غیرممکن است. استفاده از این تنظیم برای موارد با هدف خاص در نظر گرفته شده است و بسیاری از انتظارات کاربر را هنگامی که برای استفاده عمومی اجرا می شود، از بین می برد.
|
||||||
custom_css: میتوانیدروی نگارش وب ماستودون سبکهای سفارشی اعمال کنید.
|
custom_css: میتوانیدروی نگارش وب ماستودون سبکهای سفارشی اعمال کنید.
|
||||||
|
|||||||
@ -88,7 +88,7 @@ fi:
|
|||||||
activity_api_enabled: Paikallisesti julkaistujen julkaisujen, aktiivisten käyttäjien ja rekisteröitymisten viikoittainen määrä
|
activity_api_enabled: Paikallisesti julkaistujen julkaisujen, aktiivisten käyttäjien ja rekisteröitymisten viikoittainen määrä
|
||||||
app_icon: WEBP, PNG, GIF tai JPG. Korvaa oletusarvoisen mobiililaitteiden sovelluskuvakkeen haluamallasi kuvakkeella.
|
app_icon: WEBP, PNG, GIF tai JPG. Korvaa oletusarvoisen mobiililaitteiden sovelluskuvakkeen haluamallasi kuvakkeella.
|
||||||
backups_retention_period: Käyttäjillä on mahdollisuus arkistoida julkaisujaan myöhemmin ladattaviksi. Kun kentän arvo on positiivinen, nämä arkistot poistuvat automaattisesti, kun määritetty määrä päiviä on kulunut.
|
backups_retention_period: Käyttäjillä on mahdollisuus arkistoida julkaisujaan myöhemmin ladattaviksi. Kun kentän arvo on positiivinen, nämä arkistot poistuvat automaattisesti, kun määritetty määrä päiviä on kulunut.
|
||||||
bootstrap_timeline_accounts: Nämä tilit kiinnitetään uusien käyttäjien seurantasuositusten alkuun.
|
bootstrap_timeline_accounts: Nämä tilit kiinnittyvät uusien käyttäjien seurantasuositusten alkuun. Syötä pilkuin eroteltu luettelo tilejä.
|
||||||
closed_registrations_message: Näkyy, kun rekisteröityminen on suljettu
|
closed_registrations_message: Näkyy, kun rekisteröityminen on suljettu
|
||||||
content_cache_retention_period: Kaikki muiden palvelinten julkaisut (mukaan lukien tehostukset ja vastaukset) poistuvat, kun määritetty määrä päiviä on kulunut, lukuun ottamatta paikallisen käyttäjän vuorovaikutusta näiden julkaisujen kanssa. Tämä sisältää julkaisut, jotka paikallinen käyttäjä on merkinnyt kirjanmerkiksi tai suosikiksi. Myös yksityismaininnat eri palvelinten käyttäjien välillä menetetään, eikä niitä voi palauttaa. Tämä asetus on tarkoitettu käytettäväksi erityistapauksissa ja rikkoo monia käyttäjien odotuksia, kun sitä sovelletaan yleiskäyttöön.
|
content_cache_retention_period: Kaikki muiden palvelinten julkaisut (mukaan lukien tehostukset ja vastaukset) poistuvat, kun määritetty määrä päiviä on kulunut, lukuun ottamatta paikallisen käyttäjän vuorovaikutusta näiden julkaisujen kanssa. Tämä sisältää julkaisut, jotka paikallinen käyttäjä on merkinnyt kirjanmerkiksi tai suosikiksi. Myös yksityismaininnat eri palvelinten käyttäjien välillä menetetään, eikä niitä voi palauttaa. Tämä asetus on tarkoitettu käytettäväksi erityistapauksissa ja rikkoo monia käyttäjien odotuksia, kun sitä sovelletaan yleiskäyttöön.
|
||||||
custom_css: Voit käyttää mukautettuja tyylejä Mastodonin selainversiossa.
|
custom_css: Voit käyttää mukautettuja tyylejä Mastodonin selainversiossa.
|
||||||
|
|||||||
@ -88,7 +88,7 @@ fo:
|
|||||||
activity_api_enabled: Tal av lokalt útgivnum postum, virknum brúkarum og nýggjum skrásetingum býtt vikuliga
|
activity_api_enabled: Tal av lokalt útgivnum postum, virknum brúkarum og nýggjum skrásetingum býtt vikuliga
|
||||||
app_icon: WEBP, PNG, GIF ella JPG. Býtir vanligu ikonina á fartelefoneindum um við eina ser-ikon.
|
app_icon: WEBP, PNG, GIF ella JPG. Býtir vanligu ikonina á fartelefoneindum um við eina ser-ikon.
|
||||||
backups_retention_period: Brúkarar hava møguleika at gera trygdaravrit av teirra postum, sum tey kunnu taka niður seinni. Tá hetta er sett til eitt virði størri enn 0, so verða hesi trygdaravrit strikaði av sær sjálvum frá tínar goymslu eftir ásetta talið av døgum.
|
backups_retention_period: Brúkarar hava møguleika at gera trygdaravrit av teirra postum, sum tey kunnu taka niður seinni. Tá hetta er sett til eitt virði størri enn 0, so verða hesi trygdaravrit strikaði av sær sjálvum frá tínar goymslu eftir ásetta talið av døgum.
|
||||||
bootstrap_timeline_accounts: Hesar kontur verða festar ovast á listanum yvir brúkarar, sum tey nýggju verða mælt til at fylgja.
|
bootstrap_timeline_accounts: Hesar konturnar verða festar ovast í fylgjaratilmælunum hjá nýggjum brúkarum. Kom við einum komma-skildum lista av kontum.
|
||||||
closed_registrations_message: Víst tá stongt er fyri tilmeldingum
|
closed_registrations_message: Víst tá stongt er fyri tilmeldingum
|
||||||
content_cache_retention_period: Allir postar frá øðrum ambætarum (íroknað stimbranir og svar) verða strikaði eftir ásetta talið av døgum, óansæð hvussu lokalir brúkarar hava samvirkað við hesar postar. Hetta fevnir eisini um postar, sum lokalir brúkarar hava bókamerkt ella yndismerkt. Privatar umrøður millum brúkarar frá ymiskum ambætarum verða eisini burturmistar og ómøguligar at endurskapa. Brúk av hesi stillingini er einans hugsað til serligar støður og oyðileggur nógv, sum brúkarar vænta av einum vanligum ambætara.
|
content_cache_retention_period: Allir postar frá øðrum ambætarum (íroknað stimbranir og svar) verða strikaði eftir ásetta talið av døgum, óansæð hvussu lokalir brúkarar hava samvirkað við hesar postar. Hetta fevnir eisini um postar, sum lokalir brúkarar hava bókamerkt ella yndismerkt. Privatar umrøður millum brúkarar frá ymiskum ambætarum verða eisini burturmistar og ómøguligar at endurskapa. Brúk av hesi stillingini er einans hugsað til serligar støður og oyðileggur nógv, sum brúkarar vænta av einum vanligum ambætara.
|
||||||
custom_css: Tú kanst seta títt egna snið upp í net-útgávuni av Mastodon.
|
custom_css: Tú kanst seta títt egna snið upp í net-útgávuni av Mastodon.
|
||||||
|
|||||||
@ -82,7 +82,6 @@ fr-CA:
|
|||||||
activity_api_enabled: Nombre de messages publiés localement, de comptes actifs et de nouvelles inscriptions par tranche hebdomadaire
|
activity_api_enabled: Nombre de messages publiés localement, de comptes actifs et de nouvelles inscriptions par tranche hebdomadaire
|
||||||
app_icon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée.
|
app_icon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée.
|
||||||
backups_retention_period: Les utilisateur·rice·s ont la possibilité de générer des archives de leurs messages pour les télécharger plus tard. Lorsqu'elles sont définies à une valeur positive, ces archives seront automatiquement supprimées de votre stockage après le nombre de jours spécifié.
|
backups_retention_period: Les utilisateur·rice·s ont la possibilité de générer des archives de leurs messages pour les télécharger plus tard. Lorsqu'elles sont définies à une valeur positive, ces archives seront automatiquement supprimées de votre stockage après le nombre de jours spécifié.
|
||||||
bootstrap_timeline_accounts: Ces comptes seront épinglés en tête de liste des recommandations pour les nouveaux utilisateurs.
|
|
||||||
closed_registrations_message: Affiché lorsque les inscriptions sont fermées
|
closed_registrations_message: Affiché lorsque les inscriptions sont fermées
|
||||||
content_cache_retention_period: Tous les messages provenant d'autres serveurs (y compris les partages et les réponses) seront supprimés passé le nombre de jours spécifié, sans tenir compte de l'interaction de l'utilisateur·rice local·e avec ces messages. Cela inclut les messages qu'un·e utilisateur·rice aurait marqué comme signets ou comme favoris. Les mentions privées entre utilisateur·rice·s de différentes instances seront également perdues et impossibles à restaurer. L'utilisation de ce paramètre est destinée à des instances spécifiques et contrevient à de nombreuses attentes des utilisateurs lorsqu'elle est appliquée à des fins d'utilisation ordinaires.
|
content_cache_retention_period: Tous les messages provenant d'autres serveurs (y compris les partages et les réponses) seront supprimés passé le nombre de jours spécifié, sans tenir compte de l'interaction de l'utilisateur·rice local·e avec ces messages. Cela inclut les messages qu'un·e utilisateur·rice aurait marqué comme signets ou comme favoris. Les mentions privées entre utilisateur·rice·s de différentes instances seront également perdues et impossibles à restaurer. L'utilisation de ce paramètre est destinée à des instances spécifiques et contrevient à de nombreuses attentes des utilisateurs lorsqu'elle est appliquée à des fins d'utilisation ordinaires.
|
||||||
custom_css: Vous pouvez appliquer des styles personnalisés sur la version Web de Mastodon.
|
custom_css: Vous pouvez appliquer des styles personnalisés sur la version Web de Mastodon.
|
||||||
|
|||||||
@ -82,7 +82,6 @@ fr:
|
|||||||
activity_api_enabled: Nombre de messages publiés localement, de comptes actifs et de nouvelles inscriptions par tranche hebdomadaire
|
activity_api_enabled: Nombre de messages publiés localement, de comptes actifs et de nouvelles inscriptions par tranche hebdomadaire
|
||||||
app_icon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée.
|
app_icon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée.
|
||||||
backups_retention_period: Les utilisateur·rice·s ont la possibilité de générer des archives de leurs messages pour les télécharger plus tard. Lorsqu'elles sont définies à une valeur positive, ces archives seront automatiquement supprimées de votre stockage après le nombre de jours spécifié.
|
backups_retention_period: Les utilisateur·rice·s ont la possibilité de générer des archives de leurs messages pour les télécharger plus tard. Lorsqu'elles sont définies à une valeur positive, ces archives seront automatiquement supprimées de votre stockage après le nombre de jours spécifié.
|
||||||
bootstrap_timeline_accounts: Ces comptes seront épinglés en tête de liste des recommandations pour les nouveaux utilisateurs.
|
|
||||||
closed_registrations_message: Affiché lorsque les inscriptions sont fermées
|
closed_registrations_message: Affiché lorsque les inscriptions sont fermées
|
||||||
content_cache_retention_period: Tous les messages provenant d'autres serveurs (y compris les partages et les réponses) seront supprimés passé le nombre de jours spécifié, sans tenir compte de l'interaction de l'utilisateur·rice local·e avec ces messages. Cela inclut les messages qu'un·e utilisateur·rice aurait marqué comme signets ou comme favoris. Les mentions privées entre utilisateur·rice·s de différentes instances seront également perdues et impossibles à restaurer. L'utilisation de ce paramètre est destinée à des instances spécifiques et contrevient à de nombreuses attentes des utilisateurs lorsqu'elle est appliquée à des fins d'utilisation ordinaires.
|
content_cache_retention_period: Tous les messages provenant d'autres serveurs (y compris les partages et les réponses) seront supprimés passé le nombre de jours spécifié, sans tenir compte de l'interaction de l'utilisateur·rice local·e avec ces messages. Cela inclut les messages qu'un·e utilisateur·rice aurait marqué comme signets ou comme favoris. Les mentions privées entre utilisateur·rice·s de différentes instances seront également perdues et impossibles à restaurer. L'utilisation de ce paramètre est destinée à des instances spécifiques et contrevient à de nombreuses attentes des utilisateurs lorsqu'elle est appliquée à des fins d'utilisation ordinaires.
|
||||||
custom_css: Vous pouvez appliquer des styles personnalisés sur la version Web de Mastodon.
|
custom_css: Vous pouvez appliquer des styles personnalisés sur la version Web de Mastodon.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user