Merge pull request #3188 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 90765342a35abd6883e57f5419342f491b83e250
This commit is contained in:
commit
d72ae0dd70
@ -1 +1 @@
|
|||||||
3.4.5
|
3.4.6
|
||||||
|
|||||||
2
.storybook/preview-body.html
Normal file
2
.storybook/preview-body.html
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<html class="no-reduce-motion">
|
||||||
|
</html>
|
||||||
28
CHANGELOG.md
28
CHANGELOG.md
@ -2,6 +2,34 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [4.4.4] - 2025-09-16
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Update dependencies
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix missing memoization in `Web::PushNotificationWorker` (#36085 by @ClearlyClaire)
|
||||||
|
- Fix unresponsive areas around GIFV modals in some cases (#36059 by @ClearlyClaire)
|
||||||
|
- Fix missing `beforeUnload` confirmation when a poll is being authored (#36030 by @ClearlyClaire)
|
||||||
|
- Fix processing of remote edited statuses with new media and no text (#35970 by @unfokus)
|
||||||
|
- Fix polls not being displayed in moderation interface (#35644 and #35933 by @ThisIsMissEm)
|
||||||
|
- Fix WebUI handling of deleted quoted posts (#35909 and #35918 by @ClearlyClaire and @diondiondion)
|
||||||
|
- Fix “Edit” and “Delete & Redraft” on a poll not inserting empty option (#35892 by @ClearlyClaire)
|
||||||
|
- Fix loading of some compatibility CSS on some configurations (#35876 by @shleeable)
|
||||||
|
- Fix HttpLog not being enabled with `RAILS_LOG_LEVEL=debug` (#35833 by @mjankowski)
|
||||||
|
- Fix self-destruct scheduler behavior on some Redis setups (#35823 by @ClearlyClaire)
|
||||||
|
- Fix `tootctl admin create` not bypassing reserved username checks (#35779 by @ClearlyClaire)
|
||||||
|
- Fix interaction policy changes in implicit updates not being saved (#35751 by @ClearlyClaire)
|
||||||
|
- Fix quote revocation not being streamed (#35710 by @ClearlyClaire)
|
||||||
|
- Fix export of large user archives by enabling Zip64 (#35850 by @ClearlyClaire)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Change labels for quote policy settings (#35893 by @ClearlyClaire)
|
||||||
|
- Change standalone “Share” page to redirect to web interface after posting (#35763 by @ChaosExAnima)
|
||||||
|
|
||||||
## [4.4.3] - 2025-08-05
|
## [4.4.3] - 2025-08-05
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|||||||
@ -13,7 +13,7 @@ ARG BASE_REGISTRY="docker.io"
|
|||||||
|
|
||||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"]
|
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"]
|
||||||
# renovate: datasource=docker depName=docker.io/ruby
|
# renovate: datasource=docker depName=docker.io/ruby
|
||||||
ARG RUBY_VERSION="3.4.5"
|
ARG RUBY_VERSION="3.4.6"
|
||||||
# # Node.js version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
# # Node.js version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
||||||
# renovate: datasource=node-version depName=node
|
# renovate: datasource=node-version depName=node
|
||||||
ARG NODE_MAJOR_VERSION="22"
|
ARG NODE_MAJOR_VERSION="22"
|
||||||
|
|||||||
@ -6,15 +6,13 @@ module Api::InteractionPoliciesConcern
|
|||||||
def quote_approval_policy
|
def quote_approval_policy
|
||||||
return nil unless Mastodon::Feature.outgoing_quotes_enabled?
|
return nil unless Mastodon::Feature.outgoing_quotes_enabled?
|
||||||
|
|
||||||
case status_params[:quote_approval_policy]
|
case status_params[:quote_approval_policy].presence || current_user.setting_default_quote_policy
|
||||||
when 'public'
|
when 'public'
|
||||||
Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] << 16
|
Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] << 16
|
||||||
when 'followers'
|
when 'followers'
|
||||||
Status::QUOTE_APPROVAL_POLICY_FLAGS[:followers] << 16
|
Status::QUOTE_APPROVAL_POLICY_FLAGS[:followers] << 16
|
||||||
when 'nobody'
|
when 'nobody'
|
||||||
0
|
0
|
||||||
when nil
|
|
||||||
current_user.setting_default_quote_policy
|
|
||||||
else
|
else
|
||||||
# TODO: raise more useful message
|
# TODO: raise more useful message
|
||||||
raise ActiveRecord::RecordInvalid
|
raise ActiveRecord::RecordInvalid
|
||||||
|
|||||||
@ -0,0 +1,110 @@
|
|||||||
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||||
|
import { fn, expect } from 'storybook/test';
|
||||||
|
|
||||||
|
import { Alert } from '.';
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Components/Alert',
|
||||||
|
component: Alert,
|
||||||
|
args: {
|
||||||
|
isActive: true,
|
||||||
|
animateFrom: 'side',
|
||||||
|
title: '',
|
||||||
|
message: '',
|
||||||
|
action: '',
|
||||||
|
onActionClick: fn(),
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
isActive: {
|
||||||
|
control: 'boolean',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Animate to the active (displayed) state of the alert',
|
||||||
|
},
|
||||||
|
animateFrom: {
|
||||||
|
control: 'radio',
|
||||||
|
type: 'string',
|
||||||
|
options: ['side', 'below'],
|
||||||
|
description:
|
||||||
|
'Direction that the alert animates in from when activated. `side` is dependent on reading direction, defaulting to left in ltr languages.',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
control: 'text',
|
||||||
|
type: 'string',
|
||||||
|
description: '(Optional) title of the alert',
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
control: 'text',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Main alert text',
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
control: 'text',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Label of the alert action (requires `onActionClick` handler)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ['test'],
|
||||||
|
} satisfies Meta<typeof Alert>;
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof meta>;
|
||||||
|
|
||||||
|
export const Simple: Story = {
|
||||||
|
args: {
|
||||||
|
message: 'Post published.',
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div style={{ overflow: 'clip', padding: '1rem' }}>
|
||||||
|
<Alert {...args} />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithAction: Story = {
|
||||||
|
args: {
|
||||||
|
...Simple.args,
|
||||||
|
action: 'Open',
|
||||||
|
},
|
||||||
|
render: Simple.render,
|
||||||
|
play: async ({ args, canvas, userEvent }) => {
|
||||||
|
const button = await canvas.findByRole('button', { name: 'Open' });
|
||||||
|
await userEvent.click(button);
|
||||||
|
await expect(args.onActionClick).toHaveBeenCalled();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithTitle: Story = {
|
||||||
|
args: {
|
||||||
|
title: 'Warning:',
|
||||||
|
message: 'This is an alert',
|
||||||
|
},
|
||||||
|
render: Simple.render,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithDismissButton: Story = {
|
||||||
|
args: {
|
||||||
|
message: 'More replies found',
|
||||||
|
action: 'Show',
|
||||||
|
onDismiss: fn(),
|
||||||
|
},
|
||||||
|
render: Simple.render,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InSizedContainer: Story = {
|
||||||
|
args: WithDismissButton.args,
|
||||||
|
render: (args) => (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
overflow: 'clip',
|
||||||
|
padding: '1rem',
|
||||||
|
width: '380px',
|
||||||
|
maxWidth: '100%',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Alert {...args} />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
68
app/javascript/flavours/glitch/components/alert/index.tsx
Normal file
68
app/javascript/flavours/glitch/components/alert/index.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
|
|
||||||
|
import { IconButton } from '../icon_button';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snackbar/Toast-style notification component.
|
||||||
|
*/
|
||||||
|
export const Alert: React.FC<{
|
||||||
|
isActive?: boolean;
|
||||||
|
animateFrom?: 'side' | 'below';
|
||||||
|
title?: string;
|
||||||
|
message: string;
|
||||||
|
action?: string;
|
||||||
|
onActionClick?: () => void;
|
||||||
|
onDismiss?: () => void;
|
||||||
|
}> = ({
|
||||||
|
isActive,
|
||||||
|
animateFrom = 'side',
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
action,
|
||||||
|
onActionClick,
|
||||||
|
onDismiss,
|
||||||
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const hasAction = Boolean(action && onActionClick);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames('notification-bar', {
|
||||||
|
'notification-bar--active': isActive,
|
||||||
|
'from-side': animateFrom === 'side',
|
||||||
|
'from-below': animateFrom === 'below',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span className='notification-bar__content'>
|
||||||
|
{Boolean(title) && (
|
||||||
|
<span className='notification-bar__title'>{title}</span>
|
||||||
|
)}
|
||||||
|
{message}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{hasAction && (
|
||||||
|
<button className='notification-bar__action' onClick={onActionClick}>
|
||||||
|
{action}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{onDismiss && (
|
||||||
|
<IconButton
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'dismissable_banner.dismiss',
|
||||||
|
defaultMessage: 'Dismiss',
|
||||||
|
})}
|
||||||
|
icon='times'
|
||||||
|
iconComponent={CloseIcon}
|
||||||
|
className='notification-bar__dismiss-button'
|
||||||
|
onClick={onDismiss}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -3,16 +3,16 @@ import { useState, useEffect } from 'react';
|
|||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import type { IntlShape } from 'react-intl';
|
import type { IntlShape } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import { dismissAlert } from 'flavours/glitch/actions/alerts';
|
import { dismissAlert } from 'flavours/glitch/actions/alerts';
|
||||||
import type {
|
import type {
|
||||||
Alert,
|
Alert as AlertType,
|
||||||
TranslatableString,
|
TranslatableString,
|
||||||
TranslatableValues,
|
TranslatableValues,
|
||||||
} from 'flavours/glitch/models/alert';
|
} from 'flavours/glitch/models/alert';
|
||||||
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
||||||
|
|
||||||
|
import { Alert } from './alert';
|
||||||
|
|
||||||
const formatIfNeeded = (
|
const formatIfNeeded = (
|
||||||
intl: IntlShape,
|
intl: IntlShape,
|
||||||
message: TranslatableString,
|
message: TranslatableString,
|
||||||
@ -25,8 +25,8 @@ const formatIfNeeded = (
|
|||||||
return message;
|
return message;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Alert: React.FC<{
|
const TimedAlert: React.FC<{
|
||||||
alert: Alert;
|
alert: AlertType;
|
||||||
dismissAfter: number;
|
dismissAfter: number;
|
||||||
}> = ({
|
}> = ({
|
||||||
alert: { key, title, message, values, action, onClick },
|
alert: { key, title, message, values, action, onClick },
|
||||||
@ -62,29 +62,13 @@ const Alert: React.FC<{
|
|||||||
}, [dispatch, setActive, key, dismissAfter]);
|
}, [dispatch, setActive, key, dismissAfter]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Alert
|
||||||
className={classNames('notification-bar', {
|
isActive={active}
|
||||||
'notification-bar-active': active,
|
title={title ? formatIfNeeded(intl, title, values) : undefined}
|
||||||
})}
|
message={formatIfNeeded(intl, message, values)}
|
||||||
>
|
action={action ? formatIfNeeded(intl, action, values) : undefined}
|
||||||
<div className='notification-bar-wrapper'>
|
onActionClick={onClick}
|
||||||
{title && (
|
/>
|
||||||
<span className='notification-bar-title'>
|
|
||||||
{formatIfNeeded(intl, title, values)}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<span className='notification-bar-message'>
|
|
||||||
{formatIfNeeded(intl, message, values)}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{action && (
|
|
||||||
<button className='notification-bar-action' onClick={onClick}>
|
|
||||||
{formatIfNeeded(intl, action, values)}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,7 +82,11 @@ export const AlertsController: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className='notification-list'>
|
<div className='notification-list'>
|
||||||
{alerts.map((alert, idx) => (
|
{alerts.map((alert, idx) => (
|
||||||
<Alert key={alert.key} alert={alert} dismissAfter={5000 + idx * 1000} />
|
<TimedAlert
|
||||||
|
key={alert.key}
|
||||||
|
alert={alert}
|
||||||
|
dismissAfter={5000 + idx * 1000}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,122 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import type { List } from 'immutable';
|
|
||||||
|
|
||||||
import type { Account } from 'flavours/glitch/models/account';
|
|
||||||
|
|
||||||
import { autoPlayGif } from '../initial_state';
|
|
||||||
|
|
||||||
import { Skeleton } from './skeleton';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
account?: Account;
|
|
||||||
others?: List<Account>;
|
|
||||||
localDomain?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DisplayName extends React.PureComponent<Props> {
|
|
||||||
handleMouseEnter: React.ReactEventHandler<HTMLSpanElement> = ({
|
|
||||||
currentTarget,
|
|
||||||
}) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis =
|
|
||||||
currentTarget.querySelectorAll<HTMLImageElement>('img.custom-emoji');
|
|
||||||
|
|
||||||
emojis.forEach((emoji) => {
|
|
||||||
const originalSrc = emoji.getAttribute('data-original');
|
|
||||||
if (originalSrc != null) emoji.src = originalSrc;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
handleMouseLeave: React.ReactEventHandler<HTMLSpanElement> = ({
|
|
||||||
currentTarget,
|
|
||||||
}) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis =
|
|
||||||
currentTarget.querySelectorAll<HTMLImageElement>('img.custom-emoji');
|
|
||||||
|
|
||||||
emojis.forEach((emoji) => {
|
|
||||||
const staticSrc = emoji.getAttribute('data-static');
|
|
||||||
if (staticSrc != null) emoji.src = staticSrc;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { others, localDomain } = this.props;
|
|
||||||
|
|
||||||
let displayName: React.ReactNode,
|
|
||||||
suffix: React.ReactNode,
|
|
||||||
account: Account | undefined;
|
|
||||||
|
|
||||||
if (others && others.size > 0) {
|
|
||||||
account = others.first();
|
|
||||||
} else if (this.props.account) {
|
|
||||||
account = this.props.account;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (others && others.size > 1) {
|
|
||||||
displayName = others
|
|
||||||
.take(2)
|
|
||||||
.map((a) => (
|
|
||||||
<bdi key={a.get('id')}>
|
|
||||||
<strong
|
|
||||||
className='display-name__html'
|
|
||||||
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
))
|
|
||||||
.reduce((prev, cur) => [prev, ', ', cur]);
|
|
||||||
|
|
||||||
if (others.size - 2 > 0) {
|
|
||||||
suffix = `+${others.size - 2}`;
|
|
||||||
}
|
|
||||||
} else if (account) {
|
|
||||||
let acct = account.get('acct');
|
|
||||||
|
|
||||||
if (!acct.includes('@') && localDomain) {
|
|
||||||
acct = `${acct}@${localDomain}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
displayName = (
|
|
||||||
<bdi>
|
|
||||||
<strong
|
|
||||||
className='display-name__html'
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: account.get('display_name_html'),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
);
|
|
||||||
suffix = <span className='display-name__account'>@{acct}</span>;
|
|
||||||
} else {
|
|
||||||
displayName = (
|
|
||||||
<bdi>
|
|
||||||
<strong className='display-name__html'>
|
|
||||||
<Skeleton width='10ch' />
|
|
||||||
</strong>
|
|
||||||
</bdi>
|
|
||||||
);
|
|
||||||
suffix = (
|
|
||||||
<span className='display-name__account'>
|
|
||||||
<Skeleton width='7ch' />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
className='display-name'
|
|
||||||
onMouseEnter={this.handleMouseEnter}
|
|
||||||
onMouseLeave={this.handleMouseLeave}
|
|
||||||
>
|
|
||||||
{displayName} {suffix}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import type { ComponentPropsWithoutRef, FC } from 'react';
|
||||||
|
|
||||||
|
import { Skeleton } from '../skeleton';
|
||||||
|
|
||||||
|
import type { DisplayNameProps } from './index';
|
||||||
|
import { DisplayNameWithoutDomain } from './no-domain';
|
||||||
|
|
||||||
|
export const DisplayNameDefault: FC<
|
||||||
|
Omit<DisplayNameProps, 'variant'> & ComponentPropsWithoutRef<'span'>
|
||||||
|
> = ({ account, localDomain, className, ...props }) => {
|
||||||
|
const username = useMemo(() => {
|
||||||
|
if (!account) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let acct = account.get('acct');
|
||||||
|
|
||||||
|
if (!acct.includes('@') && localDomain) {
|
||||||
|
acct = `${acct}@${localDomain}`;
|
||||||
|
}
|
||||||
|
return `@${acct}`;
|
||||||
|
}, [account, localDomain]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DisplayNameWithoutDomain
|
||||||
|
account={account}
|
||||||
|
className={className}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
<span className='display-name__account'>
|
||||||
|
{username ?? <Skeleton width='7ch' />}
|
||||||
|
</span>
|
||||||
|
</DisplayNameWithoutDomain>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,122 +1,52 @@
|
|||||||
import type { ComponentPropsWithoutRef, FC } from 'react';
|
import type { ComponentPropsWithoutRef, FC } from 'react';
|
||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import type { LinkProps } from 'react-router-dom';
|
import type { LinkProps } from 'react-router-dom';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { EmojiHTML } from '@/flavours/glitch/features/emoji/emoji_html';
|
|
||||||
import type { Account } from '@/flavours/glitch/models/account';
|
import type { Account } from '@/flavours/glitch/models/account';
|
||||||
import { isModernEmojiEnabled } from '@/flavours/glitch/utils/environment';
|
import { Permalink } from 'flavours/glitch/components/permalink';
|
||||||
|
|
||||||
import { Skeleton } from '../skeleton';
|
import { DisplayNameDefault } from './default';
|
||||||
|
import { DisplayNameWithoutDomain } from './no-domain';
|
||||||
|
import { DisplayNameSimple } from './simple';
|
||||||
|
|
||||||
interface Props {
|
export interface DisplayNameProps {
|
||||||
account?: Account;
|
account?: Account;
|
||||||
localDomain?: string;
|
localDomain?: string;
|
||||||
simple?: boolean;
|
variant?: 'default' | 'simple' | 'noDomain';
|
||||||
noDomain?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DisplayName: FC<Props & ComponentPropsWithoutRef<'span'>> = ({
|
export const DisplayName: FC<
|
||||||
account,
|
DisplayNameProps & ComponentPropsWithoutRef<'span'>
|
||||||
localDomain,
|
> = ({ variant = 'default', ...props }) => {
|
||||||
simple = false,
|
if (variant === 'simple') {
|
||||||
noDomain = false,
|
return <DisplayNameSimple {...props} />;
|
||||||
className,
|
} else if (variant === 'noDomain') {
|
||||||
...props
|
return <DisplayNameWithoutDomain {...props} />;
|
||||||
}) => {
|
|
||||||
const username = useMemo(() => {
|
|
||||||
if (!account || noDomain) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
let acct = account.get('acct');
|
return <DisplayNameDefault {...props} />;
|
||||||
|
|
||||||
if (!acct.includes('@') && localDomain) {
|
|
||||||
acct = `${acct}@${localDomain}`;
|
|
||||||
}
|
|
||||||
return `@${acct}`;
|
|
||||||
}, [account, localDomain, noDomain]);
|
|
||||||
|
|
||||||
if (!account) {
|
|
||||||
if (simple) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<span {...props} className={classNames('display-name', className)}>
|
|
||||||
<bdi>
|
|
||||||
<strong className='display-name__html'>
|
|
||||||
<Skeleton width='10ch' />
|
|
||||||
</strong>
|
|
||||||
</bdi>
|
|
||||||
{!noDomain && (
|
|
||||||
<span className='display-name__account'>
|
|
||||||
|
|
||||||
<Skeleton width='7ch' />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const accountName = isModernEmojiEnabled()
|
|
||||||
? account.get('display_name')
|
|
||||||
: account.get('display_name_html');
|
|
||||||
if (simple) {
|
|
||||||
return (
|
|
||||||
<bdi>
|
|
||||||
<EmojiHTML {...props} htmlString={accountName} shallow as='span' />
|
|
||||||
</bdi>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span {...props} className={classNames('display-name', className)}>
|
|
||||||
<bdi>
|
|
||||||
<EmojiHTML
|
|
||||||
className='display-name__html'
|
|
||||||
htmlString={accountName}
|
|
||||||
shallow
|
|
||||||
as='strong'
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
{username && (
|
|
||||||
<span className='display-name__account'> {username}</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LinkedDisplayName: FC<
|
export const LinkedDisplayName: FC<
|
||||||
Props & { asProps?: ComponentPropsWithoutRef<'span'> } & Partial<LinkProps>
|
Omit<LinkProps, 'to'> & {
|
||||||
> = ({
|
displayProps: DisplayNameProps & ComponentPropsWithoutRef<'span'>;
|
||||||
account,
|
}
|
||||||
asProps = {},
|
> = ({ displayProps, children, ...linkProps }) => {
|
||||||
className,
|
const { account } = displayProps;
|
||||||
localDomain,
|
|
||||||
simple,
|
|
||||||
noDomain,
|
|
||||||
...linkProps
|
|
||||||
}) => {
|
|
||||||
const displayProps = {
|
|
||||||
account,
|
|
||||||
className,
|
|
||||||
localDomain,
|
|
||||||
simple,
|
|
||||||
noDomain,
|
|
||||||
...asProps,
|
|
||||||
};
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return <DisplayName {...displayProps} />;
|
return <DisplayName {...displayProps} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Permalink
|
||||||
|
href={account.url}
|
||||||
to={`/@${account.acct}`}
|
to={`/@${account.acct}`}
|
||||||
title={`@${account.acct}`}
|
title={`@${account.acct}`}
|
||||||
|
data-id={account.id}
|
||||||
data-hover-card-account={account.id}
|
data-hover-card-account={account.id}
|
||||||
{...linkProps}
|
{...linkProps}
|
||||||
>
|
>
|
||||||
|
{children}
|
||||||
<DisplayName {...displayProps} />
|
<DisplayName {...displayProps} />
|
||||||
</Link>
|
</Permalink>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
import type { ComponentPropsWithoutRef, FC } from 'react';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { EmojiHTML } from '@/flavours/glitch/features/emoji/emoji_html';
|
||||||
|
import { isModernEmojiEnabled } from '@/flavours/glitch/utils/environment';
|
||||||
|
|
||||||
|
import { Skeleton } from '../skeleton';
|
||||||
|
|
||||||
|
import type { DisplayNameProps } from './index';
|
||||||
|
|
||||||
|
export const DisplayNameWithoutDomain: FC<
|
||||||
|
Omit<DisplayNameProps, 'variant' | 'localDomain'> &
|
||||||
|
ComponentPropsWithoutRef<'span'>
|
||||||
|
> = ({ account, className, children, ...props }) => {
|
||||||
|
return (
|
||||||
|
<span {...props} className={classNames('display-name', className)}>
|
||||||
|
<bdi>
|
||||||
|
{account ? (
|
||||||
|
<EmojiHTML
|
||||||
|
className='display-name__html'
|
||||||
|
htmlString={
|
||||||
|
isModernEmojiEnabled()
|
||||||
|
? account.get('display_name')
|
||||||
|
: account.get('display_name_html')
|
||||||
|
}
|
||||||
|
shallow
|
||||||
|
as='strong'
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<strong className='display-name__html'>
|
||||||
|
<Skeleton width='10ch' />
|
||||||
|
</strong>
|
||||||
|
)}
|
||||||
|
</bdi>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import type { ComponentPropsWithoutRef, FC } from 'react';
|
||||||
|
|
||||||
|
import { EmojiHTML } from '@/flavours/glitch/features/emoji/emoji_html';
|
||||||
|
import { isModernEmojiEnabled } from '@/flavours/glitch/utils/environment';
|
||||||
|
|
||||||
|
import type { DisplayNameProps } from './index';
|
||||||
|
|
||||||
|
export const DisplayNameSimple: FC<
|
||||||
|
Omit<DisplayNameProps, 'variant' | 'localDomain'> &
|
||||||
|
ComponentPropsWithoutRef<'span'>
|
||||||
|
> = ({ account, ...props }) => {
|
||||||
|
if (!account) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const accountName = isModernEmojiEnabled()
|
||||||
|
? account.get('display_name')
|
||||||
|
: account.get('display_name_html');
|
||||||
|
return (
|
||||||
|
<bdi>
|
||||||
|
<EmojiHTML {...props} htmlString={accountName} shallow as='span' />
|
||||||
|
</bdi>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -25,10 +25,9 @@ import { displayMedia } from '../initial_state';
|
|||||||
import AttachmentList from './attachment_list';
|
import AttachmentList from './attachment_list';
|
||||||
import { Avatar } from './avatar';
|
import { Avatar } from './avatar';
|
||||||
import { AvatarOverlay } from './avatar_overlay';
|
import { AvatarOverlay } from './avatar_overlay';
|
||||||
import { DisplayName } from './display_name';
|
import { LinkedDisplayName } from './display_name';
|
||||||
import { getHashtagBarForStatus } from './hashtag_bar';
|
import { getHashtagBarForStatus } from './hashtag_bar';
|
||||||
import { MentionsPlaceholder } from './mentions_placeholder';
|
import { MentionsPlaceholder } from './mentions_placeholder';
|
||||||
import { Permalink } from './permalink';
|
|
||||||
import StatusActionBar from './status_action_bar';
|
import StatusActionBar from './status_action_bar';
|
||||||
import StatusContent from './status_content';
|
import StatusContent from './status_content';
|
||||||
import StatusIcons from './status_icons';
|
import StatusIcons from './status_icons';
|
||||||
@ -713,14 +712,11 @@ class Status extends ImmutablePureComponent {
|
|||||||
|
|
||||||
{(!muted) && (
|
{(!muted) && (
|
||||||
<header onClick={this.handleHeaderClick} onAuxClick={this.handleHeaderClick} className='status__info'>
|
<header onClick={this.handleHeaderClick} onAuxClick={this.handleHeaderClick} className='status__info'>
|
||||||
<Permalink href={status.getIn(['account', 'url'])} to={`/@${status.getIn(['account', 'acct'])}`} title={status.getIn(['account', 'acct'])} data-hover-card-account={status.getIn(['account', 'id'])} className='status__display-name'>
|
<LinkedDisplayName displayProps={{account: status.get('account')}} className='status__display-name'>
|
||||||
<div className='status__avatar'>
|
<div className='status__avatar'>
|
||||||
{statusAvatar}
|
{statusAvatar}
|
||||||
</div>
|
</div>
|
||||||
|
</LinkedDisplayName>
|
||||||
<DisplayName account={status.get('account')} />
|
|
||||||
</Permalink>
|
|
||||||
|
|
||||||
|
|
||||||
{isQuotedPost && !!this.props.onQuoteCancel ? (
|
{isQuotedPost && !!this.props.onQuoteCancel ? (
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|||||||
@ -2,9 +2,10 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
|
|
||||||
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { DisplayedName } from 'flavours/glitch/features/notifications_v2/components/displayed_name';
|
|
||||||
import { useAppSelector } from 'flavours/glitch/store';
|
import { useAppSelector } from 'flavours/glitch/store';
|
||||||
|
|
||||||
|
import { LinkedDisplayName } from './display_name';
|
||||||
|
|
||||||
export const StatusThreadLabel: React.FC<{
|
export const StatusThreadLabel: React.FC<{
|
||||||
accountId: string;
|
accountId: string;
|
||||||
inReplyToAccountId: string;
|
inReplyToAccountId: string;
|
||||||
@ -27,7 +28,13 @@ export const StatusThreadLabel: React.FC<{
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='status.replied_to'
|
id='status.replied_to'
|
||||||
defaultMessage='Replied to {name}'
|
defaultMessage='Replied to {name}'
|
||||||
values={{ name: <DisplayedName accountIds={[inReplyToAccountId]} /> }}
|
values={{
|
||||||
|
name: (
|
||||||
|
<LinkedDisplayName
|
||||||
|
displayProps={{ account: inReplyToAccount, variant: 'simple' }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
import { DisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
|
|
||||||
export default class FollowRequestNote extends ImmutablePureComponent {
|
export default class FollowRequestNote extends ImmutablePureComponent {
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ export default class FollowRequestNote extends ImmutablePureComponent {
|
|||||||
return (
|
return (
|
||||||
<div className='follow-request-banner'>
|
<div className='follow-request-banner'>
|
||||||
<div className='follow-request-banner__message'>
|
<div className='follow-request-banner__message'>
|
||||||
<FormattedMessage id='account.requested_follow' defaultMessage='{name} has requested to follow you' values={{ name: <bdi><strong dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi> }} />
|
<FormattedMessage id='account.requested_follow' defaultMessage='{name} has requested to follow you' values={{ name: <DisplayName account={account} variant='simple' /> }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='follow-request-banner__action'>
|
<div className='follow-request-banner__action'>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { Helmet } from 'react-helmet';
|
|||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
import { AccountBio } from '@/flavours/glitch/components/account_bio';
|
import { AccountBio } from '@/flavours/glitch/components/account_bio';
|
||||||
|
import { DisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
@ -778,7 +779,6 @@ export const AccountHeader: React.FC<{
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayNameHtml = { __html: account.display_name_html };
|
|
||||||
const fields = account.fields;
|
const fields = account.fields;
|
||||||
const isLocal = !account.acct.includes('@');
|
const isLocal = !account.acct.includes('@');
|
||||||
const username = account.acct.split('@')[0];
|
const username = account.acct.split('@')[0];
|
||||||
@ -867,7 +867,7 @@ export const AccountHeader: React.FC<{
|
|||||||
|
|
||||||
<div className='account__header__tabs__name'>
|
<div className='account__header__tabs__name'>
|
||||||
<h1>
|
<h1>
|
||||||
<span dangerouslySetInnerHTML={displayNameHtml} />
|
<DisplayName account={account} variant='simple' />
|
||||||
<small>
|
<small>
|
||||||
<span>
|
<span>
|
||||||
@{username}
|
@{username}
|
||||||
|
|||||||
@ -1,33 +1,26 @@
|
|||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { Avatar } from '@/flavours/glitch/components/avatar';
|
import { Avatar } from '@/flavours/glitch/components/avatar';
|
||||||
import { AvatarGroup } from '@/flavours/glitch/components/avatar_group';
|
import { AvatarGroup } from '@/flavours/glitch/components/avatar_group';
|
||||||
|
import { LinkedDisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import type { Account } from '@/flavours/glitch/models/account';
|
import type { Account } from '@/flavours/glitch/models/account';
|
||||||
|
|
||||||
import { useFetchFamiliarFollowers } from '../hooks/familiar_followers';
|
import { useFetchFamiliarFollowers } from '../hooks/familiar_followers';
|
||||||
|
|
||||||
const AccountLink: React.FC<{ account?: Account }> = ({ account }) => {
|
|
||||||
if (!account) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
to={`/@${account.acct}`}
|
|
||||||
data-hover-card-account={account.id}
|
|
||||||
dangerouslySetInnerHTML={{ __html: account.display_name_html }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const FamiliarFollowersReadout: React.FC<{ familiarFollowers: Account[] }> = ({
|
const FamiliarFollowersReadout: React.FC<{ familiarFollowers: Account[] }> = ({
|
||||||
familiarFollowers,
|
familiarFollowers,
|
||||||
}) => {
|
}) => {
|
||||||
const messageData = {
|
const messageData = {
|
||||||
name1: <AccountLink account={familiarFollowers.at(0)} />,
|
name1: (
|
||||||
name2: <AccountLink account={familiarFollowers.at(1)} />,
|
<LinkedDisplayName
|
||||||
|
displayProps={{ account: familiarFollowers.at(0), variant: 'simple' }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
name2: (
|
||||||
|
<LinkedDisplayName
|
||||||
|
displayProps={{ account: familiarFollowers.at(1), variant: 'simple' }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
othersCount: familiarFollowers.length - 2,
|
othersCount: familiarFollowers.length - 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { DisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import { AvatarOverlay } from 'flavours/glitch/components/avatar_overlay';
|
import { AvatarOverlay } from 'flavours/glitch/components/avatar_overlay';
|
||||||
import { DisplayName } from 'flavours/glitch/components/display_name';
|
|
||||||
import { Permalink } from 'flavours/glitch/components/permalink';
|
import { Permalink } from 'flavours/glitch/components/permalink';
|
||||||
import { useAppSelector } from 'flavours/glitch/store';
|
import { useAppSelector } from 'flavours/glitch/store';
|
||||||
|
|
||||||
@ -19,15 +19,7 @@ export const MovedNote: React.FC<{
|
|||||||
id='account.moved_to'
|
id='account.moved_to'
|
||||||
defaultMessage='{name} has indicated that their new account is now:'
|
defaultMessage='{name} has indicated that their new account is now:'
|
||||||
values={{
|
values={{
|
||||||
name: (
|
name: <DisplayName account={from} variant='simple' />,
|
||||||
<bdi>
|
|
||||||
<strong
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: from?.display_name_html ?? '',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { useCallback } from 'react';
|
|||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { DisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import { toggleStatusSpoilers } from 'flavours/glitch/actions/statuses';
|
import { toggleStatusSpoilers } from 'flavours/glitch/actions/statuses';
|
||||||
import { DetailedStatus } from 'flavours/glitch/features/status/components/detailed_status';
|
import { DetailedStatus } from 'flavours/glitch/features/status/components/detailed_status';
|
||||||
import { me } from 'flavours/glitch/initial_state';
|
import { me } from 'flavours/glitch/initial_state';
|
||||||
@ -82,11 +83,7 @@ export const HighlightedPost: React.FC<{
|
|||||||
id='annual_report.summary.highlighted_post.possessive'
|
id='annual_report.summary.highlighted_post.possessive'
|
||||||
defaultMessage="{name}'s"
|
defaultMessage="{name}'s"
|
||||||
values={{
|
values={{
|
||||||
name: account && (
|
name: <DisplayName account={account} variant='simple' />,
|
||||||
<bdi
|
|
||||||
dangerouslySetInnerHTML={{ __html: account.display_name_html }}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</strong>
|
</strong>
|
||||||
|
|||||||
@ -20,12 +20,12 @@ import { Hotkeys } from 'flavours/glitch/components/hotkeys';
|
|||||||
import AttachmentList from 'flavours/glitch/components/attachment_list';
|
import AttachmentList from 'flavours/glitch/components/attachment_list';
|
||||||
import AvatarComposite from 'flavours/glitch/components/avatar_composite';
|
import AvatarComposite from 'flavours/glitch/components/avatar_composite';
|
||||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
import { Permalink } from 'flavours/glitch/components/permalink';
|
|
||||||
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
||||||
import StatusContent from 'flavours/glitch/components/status_content';
|
import StatusContent from 'flavours/glitch/components/status_content';
|
||||||
import { Dropdown } from 'flavours/glitch/components/dropdown_menu';
|
import { Dropdown } from 'flavours/glitch/components/dropdown_menu';
|
||||||
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
||||||
import { makeGetStatus } from 'flavours/glitch/selectors';
|
import { makeGetStatus } from 'flavours/glitch/selectors';
|
||||||
|
import { LinkedDisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
more: { id: 'status.more', defaultMessage: 'More' },
|
more: { id: 'status.more', defaultMessage: 'More' },
|
||||||
@ -149,14 +149,7 @@ export const Conversation = ({ conversation, scrollKey }) => {
|
|||||||
menu.push({ text: intl.formatMessage(messages.delete), action: handleDelete });
|
menu.push({ text: intl.formatMessage(messages.delete), action: handleDelete });
|
||||||
|
|
||||||
const names = accounts.map(a => (
|
const names = accounts.map(a => (
|
||||||
<Permalink to={`/@${a.get('acct')}`} href={a.get('url')} key={a.get('id')} data-hover-card-account={a.get('id')}>
|
<LinkedDisplayName displayProps={{account, variant: 'simple'}} key={account.get('id')} />
|
||||||
<bdi>
|
|
||||||
<strong
|
|
||||||
className='display-name__html'
|
|
||||||
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
</Permalink>
|
|
||||||
)).reduce((prev, cur) => [prev, ', ', cur]);
|
)).reduce((prev, cur) => [prev, ', ', cur]);
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||||
import { Permalink } from 'flavours/glitch/components/permalink';
|
|
||||||
import { useAppSelector } from 'flavours/glitch/store';
|
import { useAppSelector } from 'flavours/glitch/store';
|
||||||
|
import { LinkedDisplayName } from '../../../components/display_name';
|
||||||
|
|
||||||
export const AuthorLink = ({ accountId }) => {
|
export const AuthorLink = ({ accountId }) => {
|
||||||
const account = useAppSelector(state => state.getIn(['accounts', accountId]));
|
const account = useAppSelector(state => state.getIn(['accounts', accountId]));
|
||||||
@ -12,10 +12,9 @@ export const AuthorLink = ({ accountId }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Permalink href={account.get('url')} to={`/@${account.get('acct')}`} className='story__details__shared__author-link' data-hover-card-account={accountId}>
|
<LinkedDisplayName displayProps={{account}} className='story__details__shared__author-link'>
|
||||||
<Avatar account={account} size={16} />
|
<Avatar account={account} size={16} />
|
||||||
<bdi dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} />
|
</LinkedDisplayName>
|
||||||
</Permalink>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import classNames from 'classnames';
|
|||||||
import { escapeRegExp } from 'lodash';
|
import { escapeRegExp } from 'lodash';
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
|
|
||||||
|
import { DisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import { openModal, closeModal } from 'flavours/glitch/actions/modal';
|
import { openModal, closeModal } from 'flavours/glitch/actions/modal';
|
||||||
import { apiRequest } from 'flavours/glitch/api';
|
import { apiRequest } from 'flavours/glitch/api';
|
||||||
import { Button } from 'flavours/glitch/components/button';
|
import { Button } from 'flavours/glitch/components/button';
|
||||||
@ -404,15 +405,13 @@ const InteractionModal: React.FC<{
|
|||||||
url: string;
|
url: string;
|
||||||
}> = ({ accountId, url }) => {
|
}> = ({ accountId, url }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const displayNameHtml = useAppSelector(
|
|
||||||
(state) => state.accounts.get(accountId)?.display_name_html ?? '',
|
|
||||||
);
|
|
||||||
const signupUrl = useAppSelector(
|
const signupUrl = useAppSelector(
|
||||||
(state) =>
|
(state) =>
|
||||||
(state.server.getIn(['server', 'registrations', 'url'], null) ||
|
(state.server.getIn(['server', 'registrations', 'url'], null) ||
|
||||||
'/auth/sign_up') as string,
|
'/auth/sign_up') as string,
|
||||||
);
|
);
|
||||||
const name = <bdi dangerouslySetInnerHTML={{ __html: displayNameHtml }} />;
|
const account = useAppSelector((state) => state.accounts.get(accountId));
|
||||||
|
const name = <DisplayName account={account} variant='simple' />;
|
||||||
|
|
||||||
const handleSignupClick = useCallback(() => {
|
const handleSignupClick = useCallback(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
|
|||||||
@ -12,9 +12,9 @@ import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react';
|
|||||||
import PersonIcon from '@/material-icons/400-24px/person-fill.svg?react';
|
import PersonIcon from '@/material-icons/400-24px/person-fill.svg?react';
|
||||||
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
|
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
|
||||||
import { Account } from 'flavours/glitch/components/account';
|
import { Account } from 'flavours/glitch/components/account';
|
||||||
|
import { LinkedDisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { Hotkeys } from 'flavours/glitch/components/hotkeys';
|
import { Hotkeys } from 'flavours/glitch/components/hotkeys';
|
||||||
import { Permalink } from 'flavours/glitch/components/permalink';
|
|
||||||
import { StatusQuoteManager } from 'flavours/glitch/components/status_quoted';
|
import { StatusQuoteManager } from 'flavours/glitch/components/status_quoted';
|
||||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||||
|
|
||||||
@ -407,19 +407,10 @@ class Notification extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const targetAccount = report.get('target_account');
|
const targetAccount = report.get('target_account');
|
||||||
const targetDisplayNameHtml = { __html: targetAccount.get('display_name_html') };
|
const targetLink = <LinkedDisplayName
|
||||||
const targetLink = (
|
|
||||||
<bdi>
|
|
||||||
<Permalink
|
|
||||||
className='notification__display-name'
|
className='notification__display-name'
|
||||||
href={targetAccount.get('url')}
|
displayProps={{account:targetAccount, variant: 'simple'}}
|
||||||
title={targetAccount.get('acct')}
|
/>;
|
||||||
to={`/@${targetAccount.get('acct')}`}
|
|
||||||
dangerouslySetInnerHTML={targetDisplayNameHtml}
|
|
||||||
data-hover-card-account={targetAccount.get('id')}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Hotkeys handlers={this.getHandlers()}>
|
<Hotkeys handlers={this.getHandlers()}>
|
||||||
@ -441,19 +432,7 @@ class Notification extends ImmutablePureComponent {
|
|||||||
render () {
|
render () {
|
||||||
const { notification } = this.props;
|
const { notification } = this.props;
|
||||||
const account = notification.get('account');
|
const account = notification.get('account');
|
||||||
const displayNameHtml = { __html: account.get('display_name_html') };
|
const link = <LinkedDisplayName className='notification__display-name' displayProps={{account, variant: 'simple'}} />;
|
||||||
const link = (
|
|
||||||
<bdi>
|
|
||||||
<Permalink
|
|
||||||
className='notification__display-name'
|
|
||||||
href={`/@${account.get('acct')}`}
|
|
||||||
title={account.get('acct')}
|
|
||||||
to={`/@${account.get('acct')}`}
|
|
||||||
dangerouslySetInnerHTML={displayNameHtml}
|
|
||||||
data-hover-card-account={account.get('id')}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
);
|
|
||||||
|
|
||||||
switch(notification.get('type')) {
|
switch(notification.get('type')) {
|
||||||
case 'follow':
|
case 'follow':
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { acceptNotificationRequest, dismissNotificationRequest } from 'flavours/
|
|||||||
import { initReport } from 'flavours/glitch/actions/reports';
|
import { initReport } from 'flavours/glitch/actions/reports';
|
||||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||||
import { CheckBox } from 'flavours/glitch/components/check_box';
|
import { CheckBox } from 'flavours/glitch/components/check_box';
|
||||||
|
import { DisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
import { Dropdown } from 'flavours/glitch/components/dropdown_menu';
|
import { Dropdown } from 'flavours/glitch/components/dropdown_menu';
|
||||||
import { makeGetAccount } from 'flavours/glitch/selectors';
|
import { makeGetAccount } from 'flavours/glitch/selectors';
|
||||||
@ -96,7 +97,7 @@ export const NotificationRequest = ({ id, accountId, notificationsCount, checked
|
|||||||
|
|
||||||
<div className='notification-request__name'>
|
<div className='notification-request__name'>
|
||||||
<div className='notification-request__name__display-name'>
|
<div className='notification-request__name__display-name'>
|
||||||
<bdi><strong dangerouslySetInnerHTML={{ __html: account?.get('display_name_html') }} /></bdi>
|
<DisplayName account={account} variant='simple' />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span>@{account?.get('acct')}</span>
|
<span>@{account?.get('acct')}</span>
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { useAppSelector } from 'flavours/glitch/store';
|
|
||||||
|
|
||||||
export const DisplayedName: React.FC<{
|
|
||||||
accountIds: string[];
|
|
||||||
}> = ({ accountIds }) => {
|
|
||||||
const lastAccountId = accountIds[0] ?? '0';
|
|
||||||
const account = useAppSelector((state) => state.accounts.get(lastAccountId));
|
|
||||||
|
|
||||||
if (!account) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
to={`/@${account.acct}`}
|
|
||||||
title={`@${account.acct}`}
|
|
||||||
data-hover-card-account={account.id}
|
|
||||||
>
|
|
||||||
<bdi dangerouslySetInnerHTML={{ __html: account.display_name_html }} />
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -2,6 +2,7 @@ import { FormattedMessage, useIntl, defineMessages } from 'react-intl';
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { DisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react';
|
import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
||||||
@ -42,11 +43,9 @@ export const NotificationAdminReport: React.FC<{
|
|||||||
|
|
||||||
if (!account || !targetAccount) return null;
|
if (!account || !targetAccount) return null;
|
||||||
|
|
||||||
const domain = account.acct.split('@')[1];
|
|
||||||
|
|
||||||
const values = {
|
const values = {
|
||||||
name: <bdi>{domain ?? `@${account.acct}`}</bdi>,
|
name: <DisplayName account={account} variant='simple' />,
|
||||||
target: <bdi>@{targetAccount.acct}</bdi>,
|
target: <DisplayName account={targetAccount} variant='simple' />,
|
||||||
category: intl.formatMessage(messages[report.category]),
|
category: intl.formatMessage(messages[report.category]),
|
||||||
count: report.status_ids.length,
|
count: report.status_ids.length,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { JSX } from 'react';
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { LinkedDisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import { replyComposeById } from 'flavours/glitch/actions/compose';
|
import { replyComposeById } from 'flavours/glitch/actions/compose';
|
||||||
import { navigateToStatus } from 'flavours/glitch/actions/statuses';
|
import { navigateToStatus } from 'flavours/glitch/actions/statuses';
|
||||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||||
@ -14,7 +15,6 @@ import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp
|
|||||||
import { NOTIFICATIONS_GROUP_MAX_AVATARS } from 'flavours/glitch/models/notification_group';
|
import { NOTIFICATIONS_GROUP_MAX_AVATARS } from 'flavours/glitch/models/notification_group';
|
||||||
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
||||||
|
|
||||||
import { DisplayedName } from './displayed_name';
|
|
||||||
import { EmbeddedStatus } from './embedded_status';
|
import { EmbeddedStatus } from './embedded_status';
|
||||||
|
|
||||||
const AVATAR_SIZE = 28;
|
const AVATAR_SIZE = 28;
|
||||||
@ -61,15 +61,18 @@ export const NotificationGroupWithStatus: React.FC<{
|
|||||||
additionalContent,
|
additionalContent,
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const account = useAppSelector((state) =>
|
||||||
|
state.accounts.get(accountIds.at(0) ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
const label = useMemo(
|
const label = useMemo(
|
||||||
() =>
|
() =>
|
||||||
labelRenderer(
|
labelRenderer(
|
||||||
<DisplayedName accountIds={accountIds} />,
|
<LinkedDisplayName displayProps={{ account, variant: 'simple' }} />,
|
||||||
count,
|
count,
|
||||||
labelSeeMoreHref,
|
labelSeeMoreHref,
|
||||||
),
|
),
|
||||||
[labelRenderer, accountIds, count, labelSeeMoreHref],
|
[labelRenderer, account, count, labelSeeMoreHref],
|
||||||
);
|
);
|
||||||
|
|
||||||
const isPrivateMention = useAppSelector(
|
const isPrivateMention = useAppSelector(
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { useMemo } from 'react';
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { LinkedDisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import { replyComposeById } from 'flavours/glitch/actions/compose';
|
import { replyComposeById } from 'flavours/glitch/actions/compose';
|
||||||
import {
|
import {
|
||||||
toggleReblog,
|
toggleReblog,
|
||||||
@ -18,7 +19,6 @@ import { StatusQuoteManager } from 'flavours/glitch/components/status_quoted';
|
|||||||
import { getStatusHidden } from 'flavours/glitch/selectors/filters';
|
import { getStatusHidden } from 'flavours/glitch/selectors/filters';
|
||||||
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
||||||
|
|
||||||
import { DisplayedName } from './displayed_name';
|
|
||||||
import type { LabelRenderer } from './notification_group_with_status';
|
import type { LabelRenderer } from './notification_group_with_status';
|
||||||
|
|
||||||
export const NotificationWithStatus: React.FC<{
|
export const NotificationWithStatus: React.FC<{
|
||||||
@ -42,9 +42,16 @@ export const NotificationWithStatus: React.FC<{
|
|||||||
}) => {
|
}) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const account = useAppSelector((state) =>
|
||||||
|
state.accounts.get(accountIds.at(0) ?? ''),
|
||||||
|
);
|
||||||
const label = useMemo(
|
const label = useMemo(
|
||||||
() => labelRenderer(<DisplayedName accountIds={accountIds} />, count),
|
() =>
|
||||||
[labelRenderer, accountIds, count],
|
labelRenderer(
|
||||||
|
<LinkedDisplayName displayProps={{ account, variant: 'simple' }} />,
|
||||||
|
count,
|
||||||
|
),
|
||||||
|
[labelRenderer, account, count],
|
||||||
);
|
);
|
||||||
|
|
||||||
const isPrivateMention = useAppSelector(
|
const isPrivateMention = useAppSelector(
|
||||||
|
|||||||
@ -10603,7 +10603,7 @@ noscript {
|
|||||||
.notification-list {
|
.notification-list {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 2rem;
|
bottom: 2rem;
|
||||||
inset-inline-start: 0;
|
inset-inline-start: 1rem;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -10611,9 +10611,11 @@ noscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.notification-bar {
|
.notification-bar {
|
||||||
|
--alert-edge-spacing: 1rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
position: relative;
|
|
||||||
inset-inline-start: -100%;
|
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -10629,33 +10631,43 @@ noscript {
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 21px;
|
line-height: 21px;
|
||||||
|
|
||||||
&.notification-bar-active {
|
&.from-side {
|
||||||
inset-inline-start: 1rem;
|
translate: calc(
|
||||||
|
-1 * (100% + var(--alert-edge-spacing)) * var(--text-x-direction)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.from-below {
|
||||||
|
translate: 0 calc(100% + var(--alert-edge-spacing));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.notification-bar--active {
|
||||||
|
translate: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-reduce-motion & {
|
.no-reduce-motion & {
|
||||||
transition: 0.5s cubic-bezier(0.89, 0.01, 0.5, 1.1);
|
transition: 0.5s cubic-bezier(0.89, 0.01, 0.5, 1.1);
|
||||||
transform: translateZ(0);
|
will-change: translate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-bar-title {
|
.notification-bar__content {
|
||||||
margin-inline-end: 5px;
|
margin-inline-end: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-bar-title,
|
.notification-bar__title {
|
||||||
.notification-bar-action {
|
margin-inline-end: 5px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-bar-action {
|
.notification-bar__action {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 0;
|
border: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
margin-inline-start: 10px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: $blurple-300;
|
color: $blurple-300;
|
||||||
|
font-weight: 700;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
|
|
||||||
@ -10666,6 +10678,17 @@ noscript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notification-bar__dismiss-button {
|
||||||
|
margin-top: -2px;
|
||||||
|
color: rgb(from currentColor r g b / 85%);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
color: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.hashtag-header {
|
.hashtag-header {
|
||||||
border-bottom: 1px solid var(--background-border-color);
|
border-bottom: 1px solid var(--background-border-color);
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
|||||||
@ -81,7 +81,7 @@ export function normalizeStatus(status, normalOldStatus) {
|
|||||||
} else {
|
} else {
|
||||||
// If the status has a CW but no contents, treat the CW as if it were the
|
// If the status has a CW but no contents, treat the CW as if it were the
|
||||||
// status' contents, to avoid having a CW toggle with seemingly no effect.
|
// status' contents, to avoid having a CW toggle with seemingly no effect.
|
||||||
if (normalStatus.spoiler_text && !normalStatus.content) {
|
if (normalStatus.spoiler_text && !normalStatus.content && !normalStatus.quote) {
|
||||||
normalStatus.content = normalStatus.spoiler_text;
|
normalStatus.content = normalStatus.spoiler_text;
|
||||||
normalStatus.spoiler_text = '';
|
normalStatus.spoiler_text = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
||||||
|
|
||||||
exports[`<DisplayName /> > renders display name + account name 1`] = `
|
|
||||||
<span
|
|
||||||
className="display-name"
|
|
||||||
onMouseEnter={[Function]}
|
|
||||||
onMouseLeave={[Function]}
|
|
||||||
>
|
|
||||||
<bdi>
|
|
||||||
<strong
|
|
||||||
className="display-name__html"
|
|
||||||
dangerouslySetInnerHTML={
|
|
||||||
{
|
|
||||||
"__html": "<p>Foo</p>",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
|
|
||||||
<span
|
|
||||||
className="display-name__account"
|
|
||||||
>
|
|
||||||
@
|
|
||||||
bar@baz
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
`;
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
import { fromJS } from 'immutable';
|
|
||||||
|
|
||||||
import renderer from 'react-test-renderer';
|
|
||||||
|
|
||||||
import { DisplayName } from '../display_name';
|
|
||||||
|
|
||||||
describe('<DisplayName />', () => {
|
|
||||||
it('renders display name + account name', () => {
|
|
||||||
const account = fromJS({
|
|
||||||
username: 'bar',
|
|
||||||
acct: 'bar@baz',
|
|
||||||
display_name_html: '<p>Foo</p>',
|
|
||||||
});
|
|
||||||
const component = renderer.create(<DisplayName account={account} />);
|
|
||||||
const tree = component.toJSON();
|
|
||||||
|
|
||||||
expect(tree).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
110
app/javascript/mastodon/components/alert/alert.stories.tsx
Normal file
110
app/javascript/mastodon/components/alert/alert.stories.tsx
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||||
|
import { fn, expect } from 'storybook/test';
|
||||||
|
|
||||||
|
import { Alert } from '.';
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Components/Alert',
|
||||||
|
component: Alert,
|
||||||
|
args: {
|
||||||
|
isActive: true,
|
||||||
|
animateFrom: 'side',
|
||||||
|
title: '',
|
||||||
|
message: '',
|
||||||
|
action: '',
|
||||||
|
onActionClick: fn(),
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
isActive: {
|
||||||
|
control: 'boolean',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Animate to the active (displayed) state of the alert',
|
||||||
|
},
|
||||||
|
animateFrom: {
|
||||||
|
control: 'radio',
|
||||||
|
type: 'string',
|
||||||
|
options: ['side', 'below'],
|
||||||
|
description:
|
||||||
|
'Direction that the alert animates in from when activated. `side` is dependent on reading direction, defaulting to left in ltr languages.',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
control: 'text',
|
||||||
|
type: 'string',
|
||||||
|
description: '(Optional) title of the alert',
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
control: 'text',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Main alert text',
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
control: 'text',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Label of the alert action (requires `onActionClick` handler)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ['test'],
|
||||||
|
} satisfies Meta<typeof Alert>;
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof meta>;
|
||||||
|
|
||||||
|
export const Simple: Story = {
|
||||||
|
args: {
|
||||||
|
message: 'Post published.',
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div style={{ overflow: 'clip', padding: '1rem' }}>
|
||||||
|
<Alert {...args} />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithAction: Story = {
|
||||||
|
args: {
|
||||||
|
...Simple.args,
|
||||||
|
action: 'Open',
|
||||||
|
},
|
||||||
|
render: Simple.render,
|
||||||
|
play: async ({ args, canvas, userEvent }) => {
|
||||||
|
const button = await canvas.findByRole('button', { name: 'Open' });
|
||||||
|
await userEvent.click(button);
|
||||||
|
await expect(args.onActionClick).toHaveBeenCalled();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithTitle: Story = {
|
||||||
|
args: {
|
||||||
|
title: 'Warning:',
|
||||||
|
message: 'This is an alert',
|
||||||
|
},
|
||||||
|
render: Simple.render,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithDismissButton: Story = {
|
||||||
|
args: {
|
||||||
|
message: 'More replies found',
|
||||||
|
action: 'Show',
|
||||||
|
onDismiss: fn(),
|
||||||
|
},
|
||||||
|
render: Simple.render,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InSizedContainer: Story = {
|
||||||
|
args: WithDismissButton.args,
|
||||||
|
render: (args) => (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
overflow: 'clip',
|
||||||
|
padding: '1rem',
|
||||||
|
width: '380px',
|
||||||
|
maxWidth: '100%',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Alert {...args} />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
68
app/javascript/mastodon/components/alert/index.tsx
Normal file
68
app/javascript/mastodon/components/alert/index.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
|
|
||||||
|
import { IconButton } from '../icon_button';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snackbar/Toast-style notification component.
|
||||||
|
*/
|
||||||
|
export const Alert: React.FC<{
|
||||||
|
isActive?: boolean;
|
||||||
|
animateFrom?: 'side' | 'below';
|
||||||
|
title?: string;
|
||||||
|
message: string;
|
||||||
|
action?: string;
|
||||||
|
onActionClick?: () => void;
|
||||||
|
onDismiss?: () => void;
|
||||||
|
}> = ({
|
||||||
|
isActive,
|
||||||
|
animateFrom = 'side',
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
action,
|
||||||
|
onActionClick,
|
||||||
|
onDismiss,
|
||||||
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const hasAction = Boolean(action && onActionClick);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames('notification-bar', {
|
||||||
|
'notification-bar--active': isActive,
|
||||||
|
'from-side': animateFrom === 'side',
|
||||||
|
'from-below': animateFrom === 'below',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span className='notification-bar__content'>
|
||||||
|
{Boolean(title) && (
|
||||||
|
<span className='notification-bar__title'>{title}</span>
|
||||||
|
)}
|
||||||
|
{message}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{hasAction && (
|
||||||
|
<button className='notification-bar__action' onClick={onActionClick}>
|
||||||
|
{action}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{onDismiss && (
|
||||||
|
<IconButton
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'dismissable_banner.dismiss',
|
||||||
|
defaultMessage: 'Dismiss',
|
||||||
|
})}
|
||||||
|
icon='times'
|
||||||
|
iconComponent={CloseIcon}
|
||||||
|
className='notification-bar__dismiss-button'
|
||||||
|
onClick={onDismiss}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -3,16 +3,16 @@ import { useState, useEffect } from 'react';
|
|||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import type { IntlShape } from 'react-intl';
|
import type { IntlShape } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import { dismissAlert } from 'mastodon/actions/alerts';
|
import { dismissAlert } from 'mastodon/actions/alerts';
|
||||||
import type {
|
import type {
|
||||||
Alert,
|
Alert as AlertType,
|
||||||
TranslatableString,
|
TranslatableString,
|
||||||
TranslatableValues,
|
TranslatableValues,
|
||||||
} from 'mastodon/models/alert';
|
} from 'mastodon/models/alert';
|
||||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||||
|
|
||||||
|
import { Alert } from './alert';
|
||||||
|
|
||||||
const formatIfNeeded = (
|
const formatIfNeeded = (
|
||||||
intl: IntlShape,
|
intl: IntlShape,
|
||||||
message: TranslatableString,
|
message: TranslatableString,
|
||||||
@ -25,8 +25,8 @@ const formatIfNeeded = (
|
|||||||
return message;
|
return message;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Alert: React.FC<{
|
const TimedAlert: React.FC<{
|
||||||
alert: Alert;
|
alert: AlertType;
|
||||||
dismissAfter: number;
|
dismissAfter: number;
|
||||||
}> = ({
|
}> = ({
|
||||||
alert: { key, title, message, values, action, onClick },
|
alert: { key, title, message, values, action, onClick },
|
||||||
@ -62,29 +62,13 @@ const Alert: React.FC<{
|
|||||||
}, [dispatch, setActive, key, dismissAfter]);
|
}, [dispatch, setActive, key, dismissAfter]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Alert
|
||||||
className={classNames('notification-bar', {
|
isActive={active}
|
||||||
'notification-bar-active': active,
|
title={title ? formatIfNeeded(intl, title, values) : undefined}
|
||||||
})}
|
message={formatIfNeeded(intl, message, values)}
|
||||||
>
|
action={action ? formatIfNeeded(intl, action, values) : undefined}
|
||||||
<div className='notification-bar-wrapper'>
|
onActionClick={onClick}
|
||||||
{title && (
|
/>
|
||||||
<span className='notification-bar-title'>
|
|
||||||
{formatIfNeeded(intl, title, values)}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<span className='notification-bar-message'>
|
|
||||||
{formatIfNeeded(intl, message, values)}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{action && (
|
|
||||||
<button className='notification-bar-action' onClick={onClick}>
|
|
||||||
{formatIfNeeded(intl, action, values)}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,7 +82,11 @@ export const AlertsController: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className='notification-list'>
|
<div className='notification-list'>
|
||||||
{alerts.map((alert, idx) => (
|
{alerts.map((alert, idx) => (
|
||||||
<Alert key={alert.key} alert={alert} dismissAfter={5000 + idx * 1000} />
|
<TimedAlert
|
||||||
|
key={alert.key}
|
||||||
|
alert={alert}
|
||||||
|
dismissAfter={5000 + idx * 1000}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,122 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import type { List } from 'immutable';
|
|
||||||
|
|
||||||
import type { Account } from 'mastodon/models/account';
|
|
||||||
|
|
||||||
import { autoPlayGif } from '../initial_state';
|
|
||||||
|
|
||||||
import { Skeleton } from './skeleton';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
account?: Account;
|
|
||||||
others?: List<Account>;
|
|
||||||
localDomain?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DisplayName extends React.PureComponent<Props> {
|
|
||||||
handleMouseEnter: React.ReactEventHandler<HTMLSpanElement> = ({
|
|
||||||
currentTarget,
|
|
||||||
}) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis =
|
|
||||||
currentTarget.querySelectorAll<HTMLImageElement>('img.custom-emoji');
|
|
||||||
|
|
||||||
emojis.forEach((emoji) => {
|
|
||||||
const originalSrc = emoji.getAttribute('data-original');
|
|
||||||
if (originalSrc != null) emoji.src = originalSrc;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
handleMouseLeave: React.ReactEventHandler<HTMLSpanElement> = ({
|
|
||||||
currentTarget,
|
|
||||||
}) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis =
|
|
||||||
currentTarget.querySelectorAll<HTMLImageElement>('img.custom-emoji');
|
|
||||||
|
|
||||||
emojis.forEach((emoji) => {
|
|
||||||
const staticSrc = emoji.getAttribute('data-static');
|
|
||||||
if (staticSrc != null) emoji.src = staticSrc;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { others, localDomain } = this.props;
|
|
||||||
|
|
||||||
let displayName: React.ReactNode,
|
|
||||||
suffix: React.ReactNode,
|
|
||||||
account: Account | undefined;
|
|
||||||
|
|
||||||
if (others && others.size > 0) {
|
|
||||||
account = others.first();
|
|
||||||
} else if (this.props.account) {
|
|
||||||
account = this.props.account;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (others && others.size > 1) {
|
|
||||||
displayName = others
|
|
||||||
.take(2)
|
|
||||||
.map((a) => (
|
|
||||||
<bdi key={a.get('id')}>
|
|
||||||
<strong
|
|
||||||
className='display-name__html'
|
|
||||||
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
))
|
|
||||||
.reduce((prev, cur) => [prev, ', ', cur]);
|
|
||||||
|
|
||||||
if (others.size - 2 > 0) {
|
|
||||||
suffix = `+${others.size - 2}`;
|
|
||||||
}
|
|
||||||
} else if (account) {
|
|
||||||
let acct = account.get('acct');
|
|
||||||
|
|
||||||
if (!acct.includes('@') && localDomain) {
|
|
||||||
acct = `${acct}@${localDomain}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
displayName = (
|
|
||||||
<bdi>
|
|
||||||
<strong
|
|
||||||
className='display-name__html'
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: account.get('display_name_html'),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
);
|
|
||||||
suffix = <span className='display-name__account'>@{acct}</span>;
|
|
||||||
} else {
|
|
||||||
displayName = (
|
|
||||||
<bdi>
|
|
||||||
<strong className='display-name__html'>
|
|
||||||
<Skeleton width='10ch' />
|
|
||||||
</strong>
|
|
||||||
</bdi>
|
|
||||||
);
|
|
||||||
suffix = (
|
|
||||||
<span className='display-name__account'>
|
|
||||||
<Skeleton width='7ch' />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
className='display-name'
|
|
||||||
onMouseEnter={this.handleMouseEnter}
|
|
||||||
onMouseLeave={this.handleMouseLeave}
|
|
||||||
>
|
|
||||||
{displayName} {suffix}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
36
app/javascript/mastodon/components/display_name/default.tsx
Normal file
36
app/javascript/mastodon/components/display_name/default.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import type { ComponentPropsWithoutRef, FC } from 'react';
|
||||||
|
|
||||||
|
import { Skeleton } from '../skeleton';
|
||||||
|
|
||||||
|
import type { DisplayNameProps } from './index';
|
||||||
|
import { DisplayNameWithoutDomain } from './no-domain';
|
||||||
|
|
||||||
|
export const DisplayNameDefault: FC<
|
||||||
|
Omit<DisplayNameProps, 'variant'> & ComponentPropsWithoutRef<'span'>
|
||||||
|
> = ({ account, localDomain, className, ...props }) => {
|
||||||
|
const username = useMemo(() => {
|
||||||
|
if (!account) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let acct = account.get('acct');
|
||||||
|
|
||||||
|
if (!acct.includes('@') && localDomain) {
|
||||||
|
acct = `${acct}@${localDomain}`;
|
||||||
|
}
|
||||||
|
return `@${acct}`;
|
||||||
|
}, [account, localDomain]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DisplayNameWithoutDomain
|
||||||
|
account={account}
|
||||||
|
className={className}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
<span className='display-name__account'>
|
||||||
|
{username ?? <Skeleton width='7ch' />}
|
||||||
|
</span>
|
||||||
|
</DisplayNameWithoutDomain>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -18,8 +18,6 @@ const meta = {
|
|||||||
username: 'mastodon@mastodon.social',
|
username: 'mastodon@mastodon.social',
|
||||||
name: 'Test User 🧪',
|
name: 'Test User 🧪',
|
||||||
loading: false,
|
loading: false,
|
||||||
simple: false,
|
|
||||||
noDomain: false,
|
|
||||||
localDomain: 'mastodon.social',
|
localDomain: 'mastodon.social',
|
||||||
},
|
},
|
||||||
tags: [],
|
tags: [],
|
||||||
@ -50,13 +48,13 @@ export const Loading: Story = {
|
|||||||
|
|
||||||
export const NoDomain: Story = {
|
export const NoDomain: Story = {
|
||||||
args: {
|
args: {
|
||||||
noDomain: true,
|
variant: 'noDomain',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Simple: Story = {
|
export const Simple: Story = {
|
||||||
args: {
|
args: {
|
||||||
simple: true,
|
variant: 'simple',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,6 +74,6 @@ export const Linked: Story = {
|
|||||||
acct: username,
|
acct: username,
|
||||||
})
|
})
|
||||||
: undefined;
|
: undefined;
|
||||||
return <LinkedDisplayName {...args} account={account} />;
|
return <LinkedDisplayName {...args} displayProps={{ account }} />;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,110 +1,37 @@
|
|||||||
import type { ComponentPropsWithoutRef, FC } from 'react';
|
import type { ComponentPropsWithoutRef, FC } from 'react';
|
||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import type { LinkProps } from 'react-router-dom';
|
import type { LinkProps } from 'react-router-dom';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { EmojiHTML } from '@/mastodon/features/emoji/emoji_html';
|
|
||||||
import type { Account } from '@/mastodon/models/account';
|
import type { Account } from '@/mastodon/models/account';
|
||||||
import { isModernEmojiEnabled } from '@/mastodon/utils/environment';
|
|
||||||
|
|
||||||
import { Skeleton } from '../skeleton';
|
import { DisplayNameDefault } from './default';
|
||||||
|
import { DisplayNameWithoutDomain } from './no-domain';
|
||||||
|
import { DisplayNameSimple } from './simple';
|
||||||
|
|
||||||
interface Props {
|
export interface DisplayNameProps {
|
||||||
account?: Account;
|
account?: Account;
|
||||||
localDomain?: string;
|
localDomain?: string;
|
||||||
simple?: boolean;
|
variant?: 'default' | 'simple' | 'noDomain';
|
||||||
noDomain?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DisplayName: FC<Props & ComponentPropsWithoutRef<'span'>> = ({
|
export const DisplayName: FC<
|
||||||
account,
|
DisplayNameProps & ComponentPropsWithoutRef<'span'>
|
||||||
localDomain,
|
> = ({ variant = 'default', ...props }) => {
|
||||||
simple = false,
|
if (variant === 'simple') {
|
||||||
noDomain = false,
|
return <DisplayNameSimple {...props} />;
|
||||||
className,
|
} else if (variant === 'noDomain') {
|
||||||
...props
|
return <DisplayNameWithoutDomain {...props} />;
|
||||||
}) => {
|
|
||||||
const username = useMemo(() => {
|
|
||||||
if (!account || noDomain) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
let acct = account.get('acct');
|
return <DisplayNameDefault {...props} />;
|
||||||
|
|
||||||
if (!acct.includes('@') && localDomain) {
|
|
||||||
acct = `${acct}@${localDomain}`;
|
|
||||||
}
|
|
||||||
return `@${acct}`;
|
|
||||||
}, [account, localDomain, noDomain]);
|
|
||||||
|
|
||||||
if (!account) {
|
|
||||||
if (simple) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<span {...props} className={classNames('display-name', className)}>
|
|
||||||
<bdi>
|
|
||||||
<strong className='display-name__html'>
|
|
||||||
<Skeleton width='10ch' />
|
|
||||||
</strong>
|
|
||||||
</bdi>
|
|
||||||
{!noDomain && (
|
|
||||||
<span className='display-name__account'>
|
|
||||||
|
|
||||||
<Skeleton width='7ch' />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const accountName = isModernEmojiEnabled()
|
|
||||||
? account.get('display_name')
|
|
||||||
: account.get('display_name_html');
|
|
||||||
if (simple) {
|
|
||||||
return (
|
|
||||||
<bdi>
|
|
||||||
<EmojiHTML {...props} htmlString={accountName} shallow as='span' />
|
|
||||||
</bdi>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span {...props} className={classNames('display-name', className)}>
|
|
||||||
<bdi>
|
|
||||||
<EmojiHTML
|
|
||||||
className='display-name__html'
|
|
||||||
htmlString={accountName}
|
|
||||||
shallow
|
|
||||||
as='strong'
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
{username && (
|
|
||||||
<span className='display-name__account'> {username}</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LinkedDisplayName: FC<
|
export const LinkedDisplayName: FC<
|
||||||
Props & { asProps?: ComponentPropsWithoutRef<'span'> } & Partial<LinkProps>
|
Omit<LinkProps, 'to'> & {
|
||||||
> = ({
|
displayProps: DisplayNameProps & ComponentPropsWithoutRef<'span'>;
|
||||||
account,
|
}
|
||||||
asProps = {},
|
> = ({ displayProps, children, ...linkProps }) => {
|
||||||
className,
|
const { account } = displayProps;
|
||||||
localDomain,
|
|
||||||
simple,
|
|
||||||
noDomain,
|
|
||||||
...linkProps
|
|
||||||
}) => {
|
|
||||||
const displayProps = {
|
|
||||||
account,
|
|
||||||
className,
|
|
||||||
localDomain,
|
|
||||||
simple,
|
|
||||||
noDomain,
|
|
||||||
...asProps,
|
|
||||||
};
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return <DisplayName {...displayProps} />;
|
return <DisplayName {...displayProps} />;
|
||||||
}
|
}
|
||||||
@ -113,9 +40,11 @@ export const LinkedDisplayName: FC<
|
|||||||
<Link
|
<Link
|
||||||
to={`/@${account.acct}`}
|
to={`/@${account.acct}`}
|
||||||
title={`@${account.acct}`}
|
title={`@${account.acct}`}
|
||||||
|
data-id={account.id}
|
||||||
data-hover-card-account={account.id}
|
data-hover-card-account={account.id}
|
||||||
{...linkProps}
|
{...linkProps}
|
||||||
>
|
>
|
||||||
|
{children}
|
||||||
<DisplayName {...displayProps} />
|
<DisplayName {...displayProps} />
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
import type { ComponentPropsWithoutRef, FC } from 'react';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { EmojiHTML } from '@/mastodon/features/emoji/emoji_html';
|
||||||
|
import { isModernEmojiEnabled } from '@/mastodon/utils/environment';
|
||||||
|
|
||||||
|
import { Skeleton } from '../skeleton';
|
||||||
|
|
||||||
|
import type { DisplayNameProps } from './index';
|
||||||
|
|
||||||
|
export const DisplayNameWithoutDomain: FC<
|
||||||
|
Omit<DisplayNameProps, 'variant' | 'localDomain'> &
|
||||||
|
ComponentPropsWithoutRef<'span'>
|
||||||
|
> = ({ account, className, children, ...props }) => {
|
||||||
|
return (
|
||||||
|
<span {...props} className={classNames('display-name', className)}>
|
||||||
|
<bdi>
|
||||||
|
{account ? (
|
||||||
|
<EmojiHTML
|
||||||
|
className='display-name__html'
|
||||||
|
htmlString={
|
||||||
|
isModernEmojiEnabled()
|
||||||
|
? account.get('display_name')
|
||||||
|
: account.get('display_name_html')
|
||||||
|
}
|
||||||
|
shallow
|
||||||
|
as='strong'
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<strong className='display-name__html'>
|
||||||
|
<Skeleton width='10ch' />
|
||||||
|
</strong>
|
||||||
|
)}
|
||||||
|
</bdi>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
23
app/javascript/mastodon/components/display_name/simple.tsx
Normal file
23
app/javascript/mastodon/components/display_name/simple.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import type { ComponentPropsWithoutRef, FC } from 'react';
|
||||||
|
|
||||||
|
import { EmojiHTML } from '@/mastodon/features/emoji/emoji_html';
|
||||||
|
import { isModernEmojiEnabled } from '@/mastodon/utils/environment';
|
||||||
|
|
||||||
|
import type { DisplayNameProps } from './index';
|
||||||
|
|
||||||
|
export const DisplayNameSimple: FC<
|
||||||
|
Omit<DisplayNameProps, 'variant' | 'localDomain'> &
|
||||||
|
ComponentPropsWithoutRef<'span'>
|
||||||
|
> = ({ account, ...props }) => {
|
||||||
|
if (!account) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const accountName = isModernEmojiEnabled()
|
||||||
|
? account.get('display_name')
|
||||||
|
: account.get('display_name_html');
|
||||||
|
return (
|
||||||
|
<bdi>
|
||||||
|
<EmojiHTML {...props} htmlString={accountName} shallow as='span' />
|
||||||
|
</bdi>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -28,7 +28,7 @@ import { displayMedia } from '../initial_state';
|
|||||||
|
|
||||||
import { Avatar } from './avatar';
|
import { Avatar } from './avatar';
|
||||||
import { AvatarOverlay } from './avatar_overlay';
|
import { AvatarOverlay } from './avatar_overlay';
|
||||||
import { DisplayName } from './display_name';
|
import { LinkedDisplayName } from './display_name';
|
||||||
import { getHashtagBarForStatus } from './hashtag_bar';
|
import { getHashtagBarForStatus } from './hashtag_bar';
|
||||||
import { RelativeTimestamp } from './relative_timestamp';
|
import { RelativeTimestamp } from './relative_timestamp';
|
||||||
import StatusActionBar from './status_action_bar';
|
import StatusActionBar from './status_action_bar';
|
||||||
@ -409,12 +409,20 @@ class Status extends ImmutablePureComponent {
|
|||||||
const matchedFilters = status.get('matched_filters');
|
const matchedFilters = status.get('matched_filters');
|
||||||
|
|
||||||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||||
const display_name_html = { __html: status.getIn(['account', 'display_name_html']) };
|
const name = (
|
||||||
|
<LinkedDisplayName
|
||||||
|
displayProps={{
|
||||||
|
account: status.get('account'),
|
||||||
|
variant: 'simple'
|
||||||
|
}}
|
||||||
|
className='status__display-name muted'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
prepend = (
|
prepend = (
|
||||||
<div className='status__prepend'>
|
<div className='status__prepend'>
|
||||||
<div className='status__prepend__icon'><Icon id='retweet' icon={RepeatIcon} /></div>
|
<div className='status__prepend__icon'><Icon id='retweet' icon={RepeatIcon} /></div>
|
||||||
<FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <Link data-id={status.getIn(['account', 'id'])} data-hover-card-account={status.getIn(['account', 'id'])} to={`/@${status.getIn(['account', 'acct'])}`} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></Link> }} />
|
<FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name }} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -570,13 +578,11 @@ class Status extends ImmutablePureComponent {
|
|||||||
<RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
|
<RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link to={`/@${status.getIn(['account', 'acct'])}`} title={status.getIn(['account', 'acct'])} data-hover-card-account={status.getIn(['account', 'id'])} className='status__display-name'>
|
<LinkedDisplayName displayProps={{account: status.get('account')}} className='status__display-name'>
|
||||||
<div className='status__avatar'>
|
<div className='status__avatar'>
|
||||||
{statusAvatar}
|
{statusAvatar}
|
||||||
</div>
|
</div>
|
||||||
|
</LinkedDisplayName>
|
||||||
<DisplayName account={status.get('account')} />
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{isQuotedPost && !!this.props.onQuoteCancel && (
|
{isQuotedPost && !!this.props.onQuoteCancel && (
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|||||||
@ -2,9 +2,10 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
|
|
||||||
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { DisplayedName } from 'mastodon/features/notifications_v2/components/displayed_name';
|
|
||||||
import { useAppSelector } from 'mastodon/store';
|
import { useAppSelector } from 'mastodon/store';
|
||||||
|
|
||||||
|
import { LinkedDisplayName } from './display_name';
|
||||||
|
|
||||||
export const StatusThreadLabel: React.FC<{
|
export const StatusThreadLabel: React.FC<{
|
||||||
accountId: string;
|
accountId: string;
|
||||||
inReplyToAccountId: string;
|
inReplyToAccountId: string;
|
||||||
@ -27,7 +28,13 @@ export const StatusThreadLabel: React.FC<{
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='status.replied_to'
|
id='status.replied_to'
|
||||||
defaultMessage='Replied to {name}'
|
defaultMessage='Replied to {name}'
|
||||||
values={{ name: <DisplayedName accountIds={[inReplyToAccountId]} /> }}
|
values={{
|
||||||
|
name: (
|
||||||
|
<LinkedDisplayName
|
||||||
|
displayProps={{ account: inReplyToAccount, variant: 'simple' }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
import { DisplayName } from '@/mastodon/components/display_name';
|
||||||
|
|
||||||
export default class FollowRequestNote extends ImmutablePureComponent {
|
export default class FollowRequestNote extends ImmutablePureComponent {
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ export default class FollowRequestNote extends ImmutablePureComponent {
|
|||||||
return (
|
return (
|
||||||
<div className='follow-request-banner'>
|
<div className='follow-request-banner'>
|
||||||
<div className='follow-request-banner__message'>
|
<div className='follow-request-banner__message'>
|
||||||
<FormattedMessage id='account.requested_follow' defaultMessage='{name} has requested to follow you' values={{ name: <bdi><strong dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi> }} />
|
<FormattedMessage id='account.requested_follow' defaultMessage='{name} has requested to follow you' values={{ name: <DisplayName account={account} variant='simple' /> }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='follow-request-banner__action'>
|
<div className='follow-request-banner__action'>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { Helmet } from 'react-helmet';
|
|||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
import { AccountBio } from '@/mastodon/components/account_bio';
|
import { AccountBio } from '@/mastodon/components/account_bio';
|
||||||
|
import { DisplayName } from '@/mastodon/components/display_name';
|
||||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
@ -774,7 +775,6 @@ export const AccountHeader: React.FC<{
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayNameHtml = { __html: account.display_name_html };
|
|
||||||
const fields = account.fields;
|
const fields = account.fields;
|
||||||
const isLocal = !account.acct.includes('@');
|
const isLocal = !account.acct.includes('@');
|
||||||
const username = account.acct.split('@')[0];
|
const username = account.acct.split('@')[0];
|
||||||
@ -863,7 +863,7 @@ export const AccountHeader: React.FC<{
|
|||||||
|
|
||||||
<div className='account__header__tabs__name'>
|
<div className='account__header__tabs__name'>
|
||||||
<h1>
|
<h1>
|
||||||
<span dangerouslySetInnerHTML={displayNameHtml} />
|
<DisplayName account={account} variant='simple' />
|
||||||
<small>
|
<small>
|
||||||
<span>
|
<span>
|
||||||
@{username}
|
@{username}
|
||||||
|
|||||||
@ -1,33 +1,26 @@
|
|||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { Avatar } from '@/mastodon/components/avatar';
|
import { Avatar } from '@/mastodon/components/avatar';
|
||||||
import { AvatarGroup } from '@/mastodon/components/avatar_group';
|
import { AvatarGroup } from '@/mastodon/components/avatar_group';
|
||||||
|
import { LinkedDisplayName } from '@/mastodon/components/display_name';
|
||||||
import type { Account } from '@/mastodon/models/account';
|
import type { Account } from '@/mastodon/models/account';
|
||||||
|
|
||||||
import { useFetchFamiliarFollowers } from '../hooks/familiar_followers';
|
import { useFetchFamiliarFollowers } from '../hooks/familiar_followers';
|
||||||
|
|
||||||
const AccountLink: React.FC<{ account?: Account }> = ({ account }) => {
|
|
||||||
if (!account) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
to={`/@${account.acct}`}
|
|
||||||
data-hover-card-account={account.id}
|
|
||||||
dangerouslySetInnerHTML={{ __html: account.display_name_html }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const FamiliarFollowersReadout: React.FC<{ familiarFollowers: Account[] }> = ({
|
const FamiliarFollowersReadout: React.FC<{ familiarFollowers: Account[] }> = ({
|
||||||
familiarFollowers,
|
familiarFollowers,
|
||||||
}) => {
|
}) => {
|
||||||
const messageData = {
|
const messageData = {
|
||||||
name1: <AccountLink account={familiarFollowers.at(0)} />,
|
name1: (
|
||||||
name2: <AccountLink account={familiarFollowers.at(1)} />,
|
<LinkedDisplayName
|
||||||
|
displayProps={{ account: familiarFollowers.at(0), variant: 'simple' }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
name2: (
|
||||||
|
<LinkedDisplayName
|
||||||
|
displayProps={{ account: familiarFollowers.at(1), variant: 'simple' }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
othersCount: familiarFollowers.length - 2,
|
othersCount: familiarFollowers.length - 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { DisplayName } from '@/mastodon/components/display_name';
|
||||||
import { AvatarOverlay } from 'mastodon/components/avatar_overlay';
|
import { AvatarOverlay } from 'mastodon/components/avatar_overlay';
|
||||||
import { DisplayName } from 'mastodon/components/display_name';
|
|
||||||
import { useAppSelector } from 'mastodon/store';
|
import { useAppSelector } from 'mastodon/store';
|
||||||
|
|
||||||
export const MovedNote: React.FC<{
|
export const MovedNote: React.FC<{
|
||||||
@ -20,15 +20,7 @@ export const MovedNote: React.FC<{
|
|||||||
id='account.moved_to'
|
id='account.moved_to'
|
||||||
defaultMessage='{name} has indicated that their new account is now:'
|
defaultMessage='{name} has indicated that their new account is now:'
|
||||||
values={{
|
values={{
|
||||||
name: (
|
name: <DisplayName account={from} variant='simple' />,
|
||||||
<bdi>
|
|
||||||
<strong
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: from?.display_name_html ?? '',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { useCallback } from 'react';
|
|||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { DisplayName } from '@/mastodon/components/display_name';
|
||||||
import { toggleStatusSpoilers } from 'mastodon/actions/statuses';
|
import { toggleStatusSpoilers } from 'mastodon/actions/statuses';
|
||||||
import { DetailedStatus } from 'mastodon/features/status/components/detailed_status';
|
import { DetailedStatus } from 'mastodon/features/status/components/detailed_status';
|
||||||
import { me } from 'mastodon/initial_state';
|
import { me } from 'mastodon/initial_state';
|
||||||
@ -79,11 +80,7 @@ export const HighlightedPost: React.FC<{
|
|||||||
id='annual_report.summary.highlighted_post.possessive'
|
id='annual_report.summary.highlighted_post.possessive'
|
||||||
defaultMessage="{name}'s"
|
defaultMessage="{name}'s"
|
||||||
values={{
|
values={{
|
||||||
name: account && (
|
name: <DisplayName account={account} variant='simple' />,
|
||||||
<bdi
|
|
||||||
dangerouslySetInnerHTML={{ __html: account.display_name_html }}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</strong>
|
</strong>
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import StatusContent from 'mastodon/components/status_content';
|
|||||||
import { Dropdown } from 'mastodon/components/dropdown_menu';
|
import { Dropdown } from 'mastodon/components/dropdown_menu';
|
||||||
import { autoPlayGif } from 'mastodon/initial_state';
|
import { autoPlayGif } from 'mastodon/initial_state';
|
||||||
import { makeGetStatus } from 'mastodon/selectors';
|
import { makeGetStatus } from 'mastodon/selectors';
|
||||||
|
import { LinkedDisplayName } from '@/mastodon/components/display_name';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
more: { id: 'status.more', defaultMessage: 'More' },
|
more: { id: 'status.more', defaultMessage: 'More' },
|
||||||
@ -139,15 +140,8 @@ export const Conversation = ({ conversation, scrollKey }) => {
|
|||||||
|
|
||||||
menu.push({ text: intl.formatMessage(messages.delete), action: handleDelete });
|
menu.push({ text: intl.formatMessage(messages.delete), action: handleDelete });
|
||||||
|
|
||||||
const names = accounts.map(a => (
|
const names = accounts.map((account) => (
|
||||||
<Link to={`/@${a.get('acct')}`} key={a.get('id')} data-hover-card-account={a.get('id')}>
|
<LinkedDisplayName displayProps={{account, variant: 'simple'}} key={account.get('id')} />
|
||||||
<bdi>
|
|
||||||
<strong
|
|
||||||
className='display-name__html'
|
|
||||||
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
</Link>
|
|
||||||
)).reduce((prev, cur) => [prev, ', ', cur]);
|
)).reduce((prev, cur) => [prev, ', ', cur]);
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
import { useAppSelector } from 'mastodon/store';
|
import { useAppSelector } from 'mastodon/store';
|
||||||
|
import { LinkedDisplayName } from '@/mastodon/components/display_name';
|
||||||
|
|
||||||
export const AuthorLink = ({ accountId }) => {
|
export const AuthorLink = ({ accountId }) => {
|
||||||
const account = useAppSelector(state => state.getIn(['accounts', accountId]));
|
const account = useAppSelector(state => state.getIn(['accounts', accountId]));
|
||||||
@ -13,10 +12,9 @@ export const AuthorLink = ({ accountId }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={`/@${account.get('acct')}`} className='story__details__shared__author-link' data-hover-card-account={accountId}>
|
<LinkedDisplayName displayProps={{account}} className='story__details__shared__author-link'>
|
||||||
<Avatar account={account} size={16} />
|
<Avatar account={account} size={16} />
|
||||||
<bdi dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} />
|
</LinkedDisplayName>
|
||||||
</Link>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import classNames from 'classnames';
|
|||||||
import { escapeRegExp } from 'lodash';
|
import { escapeRegExp } from 'lodash';
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
|
|
||||||
|
import { DisplayName } from '@/mastodon/components/display_name';
|
||||||
import { openModal, closeModal } from 'mastodon/actions/modal';
|
import { openModal, closeModal } from 'mastodon/actions/modal';
|
||||||
import { apiRequest } from 'mastodon/api';
|
import { apiRequest } from 'mastodon/api';
|
||||||
import { Button } from 'mastodon/components/button';
|
import { Button } from 'mastodon/components/button';
|
||||||
@ -404,15 +405,13 @@ const InteractionModal: React.FC<{
|
|||||||
url: string;
|
url: string;
|
||||||
}> = ({ accountId, url }) => {
|
}> = ({ accountId, url }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const displayNameHtml = useAppSelector(
|
|
||||||
(state) => state.accounts.get(accountId)?.display_name_html ?? '',
|
|
||||||
);
|
|
||||||
const signupUrl = useAppSelector(
|
const signupUrl = useAppSelector(
|
||||||
(state) =>
|
(state) =>
|
||||||
(state.server.getIn(['server', 'registrations', 'url'], null) ||
|
(state.server.getIn(['server', 'registrations', 'url'], null) ||
|
||||||
'/auth/sign_up') as string,
|
'/auth/sign_up') as string,
|
||||||
);
|
);
|
||||||
const name = <bdi dangerouslySetInnerHTML={{ __html: displayNameHtml }} />;
|
const account = useAppSelector((state) => state.accounts.get(accountId));
|
||||||
|
const name = <DisplayName account={account} variant='simple' />;
|
||||||
|
|
||||||
const handleSignupClick = useCallback(() => {
|
const handleSignupClick = useCallback(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
|
|||||||
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
||||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||||
import { Account } from 'mastodon/components/account';
|
import { Account } from 'mastodon/components/account';
|
||||||
|
import { LinkedDisplayName } from '@/mastodon/components/display_name';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { Hotkeys } from 'mastodon/components/hotkeys';
|
import { Hotkeys } from 'mastodon/components/hotkeys';
|
||||||
import { StatusQuoteManager } from 'mastodon/components/status_quoted';
|
import { StatusQuoteManager } from 'mastodon/components/status_quoted';
|
||||||
@ -485,8 +486,10 @@ class Notification extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const targetAccount = report.get('target_account');
|
const targetAccount = report.get('target_account');
|
||||||
const targetDisplayNameHtml = { __html: targetAccount.get('display_name_html') };
|
const targetLink = <LinkedDisplayName
|
||||||
const targetLink = <bdi><Link className='notification__display-name' title={targetAccount.get('acct')} data-hover-card-account={targetAccount.get('id')} to={`/@${targetAccount.get('acct')}`} dangerouslySetInnerHTML={targetDisplayNameHtml} /></bdi>;
|
className='notification__display-name'
|
||||||
|
displayProps={{account:targetAccount, variant: 'simple'}}
|
||||||
|
/>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Hotkeys handlers={this.getHandlers()}>
|
<Hotkeys handlers={this.getHandlers()}>
|
||||||
@ -508,8 +511,7 @@ class Notification extends ImmutablePureComponent {
|
|||||||
render () {
|
render () {
|
||||||
const { notification } = this.props;
|
const { notification } = this.props;
|
||||||
const account = notification.get('account');
|
const account = notification.get('account');
|
||||||
const displayNameHtml = { __html: account.get('display_name_html') };
|
const link = <LinkedDisplayName className='notification__display-name' displayProps={{account, variant: 'simple'}} />;
|
||||||
const link = <bdi><Link className='notification__display-name' href={`/@${account.get('acct')}`} title={account.get('acct')} data-hover-card-account={account.get('id')} to={`/@${account.get('acct')}`} dangerouslySetInnerHTML={displayNameHtml} /></bdi>;
|
|
||||||
|
|
||||||
switch(notification.get('type')) {
|
switch(notification.get('type')) {
|
||||||
case 'follow':
|
case 'follow':
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { acceptNotificationRequest, dismissNotificationRequest } from 'mastodon/
|
|||||||
import { initReport } from 'mastodon/actions/reports';
|
import { initReport } from 'mastodon/actions/reports';
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
import { CheckBox } from 'mastodon/components/check_box';
|
import { CheckBox } from 'mastodon/components/check_box';
|
||||||
|
import { DisplayName } from '@/mastodon/components/display_name';
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
import { Dropdown } from 'mastodon/components/dropdown_menu';
|
import { Dropdown } from 'mastodon/components/dropdown_menu';
|
||||||
import { makeGetAccount } from 'mastodon/selectors';
|
import { makeGetAccount } from 'mastodon/selectors';
|
||||||
@ -96,7 +97,7 @@ export const NotificationRequest = ({ id, accountId, notificationsCount, checked
|
|||||||
|
|
||||||
<div className='notification-request__name'>
|
<div className='notification-request__name'>
|
||||||
<div className='notification-request__name__display-name'>
|
<div className='notification-request__name__display-name'>
|
||||||
<bdi><strong dangerouslySetInnerHTML={{ __html: account?.get('display_name_html') }} /></bdi>
|
<DisplayName account={account} variant='simple' />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span>@{account?.get('acct')}</span>
|
<span>@{account?.get('acct')}</span>
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { useAppSelector } from 'mastodon/store';
|
|
||||||
|
|
||||||
export const DisplayedName: React.FC<{
|
|
||||||
accountIds: string[];
|
|
||||||
}> = ({ accountIds }) => {
|
|
||||||
const lastAccountId = accountIds[0] ?? '0';
|
|
||||||
const account = useAppSelector((state) => state.accounts.get(lastAccountId));
|
|
||||||
|
|
||||||
if (!account) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
to={`/@${account.acct}`}
|
|
||||||
title={`@${account.acct}`}
|
|
||||||
data-hover-card-account={account.id}
|
|
||||||
>
|
|
||||||
<bdi dangerouslySetInnerHTML={{ __html: account.display_name_html }} />
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -2,6 +2,7 @@ import { FormattedMessage, useIntl, defineMessages } from 'react-intl';
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { DisplayName } from '@/mastodon/components/display_name';
|
||||||
import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react';
|
import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||||
@ -42,11 +43,9 @@ export const NotificationAdminReport: React.FC<{
|
|||||||
|
|
||||||
if (!account || !targetAccount) return null;
|
if (!account || !targetAccount) return null;
|
||||||
|
|
||||||
const domain = account.acct.split('@')[1];
|
|
||||||
|
|
||||||
const values = {
|
const values = {
|
||||||
name: <bdi>{domain ?? `@${account.acct}`}</bdi>,
|
name: <DisplayName account={account} variant='simple' />,
|
||||||
target: <bdi>@{targetAccount.acct}</bdi>,
|
target: <DisplayName account={targetAccount} variant='simple' />,
|
||||||
category: intl.formatMessage(messages[report.category]),
|
category: intl.formatMessage(messages[report.category]),
|
||||||
count: report.status_ids.length,
|
count: report.status_ids.length,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { JSX } from 'react';
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { LinkedDisplayName } from '@/mastodon/components/display_name';
|
||||||
import { replyComposeById } from 'mastodon/actions/compose';
|
import { replyComposeById } from 'mastodon/actions/compose';
|
||||||
import { navigateToStatus } from 'mastodon/actions/statuses';
|
import { navigateToStatus } from 'mastodon/actions/statuses';
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
@ -14,7 +15,6 @@ import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
|||||||
import { NOTIFICATIONS_GROUP_MAX_AVATARS } from 'mastodon/models/notification_group';
|
import { NOTIFICATIONS_GROUP_MAX_AVATARS } from 'mastodon/models/notification_group';
|
||||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||||
|
|
||||||
import { DisplayedName } from './displayed_name';
|
|
||||||
import { EmbeddedStatus } from './embedded_status';
|
import { EmbeddedStatus } from './embedded_status';
|
||||||
|
|
||||||
const AVATAR_SIZE = 28;
|
const AVATAR_SIZE = 28;
|
||||||
@ -61,15 +61,18 @@ export const NotificationGroupWithStatus: React.FC<{
|
|||||||
additionalContent,
|
additionalContent,
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const account = useAppSelector((state) =>
|
||||||
|
state.accounts.get(accountIds.at(0) ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
const label = useMemo(
|
const label = useMemo(
|
||||||
() =>
|
() =>
|
||||||
labelRenderer(
|
labelRenderer(
|
||||||
<DisplayedName accountIds={accountIds} />,
|
<LinkedDisplayName displayProps={{ account, variant: 'simple' }} />,
|
||||||
count,
|
count,
|
||||||
labelSeeMoreHref,
|
labelSeeMoreHref,
|
||||||
),
|
),
|
||||||
[labelRenderer, accountIds, count, labelSeeMoreHref],
|
[labelRenderer, account, count, labelSeeMoreHref],
|
||||||
);
|
);
|
||||||
|
|
||||||
const isPrivateMention = useAppSelector(
|
const isPrivateMention = useAppSelector(
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { useMemo } from 'react';
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { LinkedDisplayName } from '@/mastodon/components/display_name';
|
||||||
import { replyComposeById } from 'mastodon/actions/compose';
|
import { replyComposeById } from 'mastodon/actions/compose';
|
||||||
import { toggleReblog, toggleFavourite } from 'mastodon/actions/interactions';
|
import { toggleReblog, toggleFavourite } from 'mastodon/actions/interactions';
|
||||||
import {
|
import {
|
||||||
@ -15,7 +16,6 @@ import { StatusQuoteManager } from 'mastodon/components/status_quoted';
|
|||||||
import { getStatusHidden } from 'mastodon/selectors/filters';
|
import { getStatusHidden } from 'mastodon/selectors/filters';
|
||||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||||
|
|
||||||
import { DisplayedName } from './displayed_name';
|
|
||||||
import type { LabelRenderer } from './notification_group_with_status';
|
import type { LabelRenderer } from './notification_group_with_status';
|
||||||
|
|
||||||
export const NotificationWithStatus: React.FC<{
|
export const NotificationWithStatus: React.FC<{
|
||||||
@ -39,9 +39,16 @@ export const NotificationWithStatus: React.FC<{
|
|||||||
}) => {
|
}) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const account = useAppSelector((state) =>
|
||||||
|
state.accounts.get(accountIds.at(0) ?? ''),
|
||||||
|
);
|
||||||
const label = useMemo(
|
const label = useMemo(
|
||||||
() => labelRenderer(<DisplayedName accountIds={accountIds} />, count),
|
() =>
|
||||||
[labelRenderer, accountIds, count],
|
labelRenderer(
|
||||||
|
<LinkedDisplayName displayProps={{ account, variant: 'simple' }} />,
|
||||||
|
count,
|
||||||
|
),
|
||||||
|
[labelRenderer, account, count],
|
||||||
);
|
);
|
||||||
|
|
||||||
const isPrivateMention = useAppSelector(
|
const isPrivateMention = useAppSelector(
|
||||||
|
|||||||
@ -849,7 +849,6 @@
|
|||||||
"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.load_new_replies": "الردود الجديدة المتاحة",
|
"status.context.load_new_replies": "الردود الجديدة المتاحة",
|
||||||
"status.context.loading": "التحقق من المزيد من الردود",
|
"status.context.loading": "التحقق من المزيد من الردود",
|
||||||
|
|||||||
@ -453,10 +453,12 @@
|
|||||||
"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>Што такое альтэрнатыўны тэкст?</h1> <p>Альтэрнатыўны тэкст апісвае відарыс людзям з парушэннем зроку, павольным злучэннем або тым, каму патрэбны дадатковы кантэкст.</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.go": "Перайсці",
|
"interaction_modal.go": "Перайсці",
|
||||||
"interaction_modal.no_account_yet": "Не маеце ўліковага запісу?",
|
"interaction_modal.no_account_yet": "Не маеце ўліковага запісу?",
|
||||||
"interaction_modal.on_another_server": "На іншым серверы",
|
"interaction_modal.on_another_server": "На іншым серверы",
|
||||||
"interaction_modal.on_this_server": "На гэтым серверы",
|
"interaction_modal.on_this_server": "На гэтым серверы",
|
||||||
|
"interaction_modal.title": "Увайдзіце, каб працягнуць",
|
||||||
"interaction_modal.username_prompt": "Напр., {example}",
|
"interaction_modal.username_prompt": "Напр., {example}",
|
||||||
"intervals.full.days": "{number, plural, one {# дзень} few {# дні} many {# дзён} other {# дня}}",
|
"intervals.full.days": "{number, plural, one {# дзень} few {# дні} many {# дзён} other {# дня}}",
|
||||||
"intervals.full.hours": "{number, plural, one {# гадзіна} few {# гадзіны} many {# гадзін} other {# гадзіны}}",
|
"intervals.full.hours": "{number, plural, one {# гадзіна} few {# гадзіны} many {# гадзін} other {# гадзіны}}",
|
||||||
@ -860,7 +862,7 @@
|
|||||||
"status.block": "Заблакіраваць @{name}",
|
"status.block": "Заблакіраваць @{name}",
|
||||||
"status.bookmark": "Дадаць закладку",
|
"status.bookmark": "Дадаць закладку",
|
||||||
"status.cancel_reblog_private": "Прыбраць",
|
"status.cancel_reblog_private": "Прыбраць",
|
||||||
"status.cannot_quote": "Цытаванне адключанае для гэтага допісу",
|
"status.cannot_quote": "Вы не маеце дазвол цытаваць гэты допіс",
|
||||||
"status.cannot_reblog": "Гэты допіс нельга пашырыць",
|
"status.cannot_reblog": "Гэты допіс нельга пашырыць",
|
||||||
"status.context.load_new_replies": "Даступныя новыя адказы",
|
"status.context.load_new_replies": "Даступныя новыя адказы",
|
||||||
"status.context.loading": "Правяраюцца новыя адказы",
|
"status.context.loading": "Правяраюцца новыя адказы",
|
||||||
|
|||||||
@ -652,7 +652,6 @@
|
|||||||
"status.block": "Stankañ @{name}",
|
"status.block": "Stankañ @{name}",
|
||||||
"status.bookmark": "Ouzhpennañ d'ar sinedoù",
|
"status.bookmark": "Ouzhpennañ d'ar sinedoù",
|
||||||
"status.cancel_reblog_private": "Nac'hañ ar skignadenn",
|
"status.cancel_reblog_private": "Nac'hañ ar skignadenn",
|
||||||
"status.cannot_quote": "N'haller ket menegiñ an embannadur-mañ",
|
|
||||||
"status.cannot_reblog": "Ar c'hannad-se na c'hall ket bezañ skignet",
|
"status.cannot_reblog": "Ar c'hannad-se na c'hall ket bezañ skignet",
|
||||||
"status.context.load_new_replies": "Respontoù nevez zo",
|
"status.context.load_new_replies": "Respontoù nevez zo",
|
||||||
"status.context.loading": "O kerc'hat muioc'h a respontoù",
|
"status.context.loading": "O kerc'hat muioc'h a respontoù",
|
||||||
|
|||||||
@ -453,10 +453,12 @@
|
|||||||
"ignore_notifications_modal.private_mentions_title": "Ignorovat oznámení z nevyžádaných soukromých zmínek?",
|
"ignore_notifications_modal.private_mentions_title": "Ignorovat oznámení z nevyžádaných soukromých zmínek?",
|
||||||
"info_button.label": "Nápověda",
|
"info_button.label": "Nápověda",
|
||||||
"info_button.what_is_alt_text": "<h1>Co je to alt text?</h1> <p>Alt text poskytuje popis obrázků pro lidi se zrakovými postižením, špatným připojením něbo těm, kteří potřebují více kontextu.</p> <p>Můžete zlepšit přístupnost a porozumění napsáním jasného, stručného a objektivního alt textu.</p> <ul> <li>Zachyťte důležité prvky</li> <li>Shrňte text v obrázku</li> <li>Použijte pravidelnou větnou skladbu</li> <li>Vyhněte se nadbytečným informacím</li> <li>U komplexních vizualizací (diagramy, mapy...) se zaměřte na trendy a klíčová zjištění</li> </ul>",
|
"info_button.what_is_alt_text": "<h1>Co je to alt text?</h1> <p>Alt text poskytuje popis obrázků pro lidi se zrakovými postižením, špatným připojením něbo těm, kteří potřebují více kontextu.</p> <p>Můžete zlepšit přístupnost a porozumění napsáním jasného, stručného a objektivního alt textu.</p> <ul> <li>Zachyťte důležité prvky</li> <li>Shrňte text v obrázku</li> <li>Použijte pravidelnou větnou skladbu</li> <li>Vyhněte se nadbytečným informacím</li> <li>U komplexních vizualizací (diagramy, mapy...) se zaměřte na trendy a klíčová zjištění</li> </ul>",
|
||||||
|
"interaction_modal.action": "Chcete-li interagovat s příspěvkem {name}, budete se muset přihlásit do vašeho účtu na Mastodon serveru, který používáte.",
|
||||||
"interaction_modal.go": "Přejít",
|
"interaction_modal.go": "Přejít",
|
||||||
"interaction_modal.no_account_yet": "Ještě nemáte účet?",
|
"interaction_modal.no_account_yet": "Ještě nemáte účet?",
|
||||||
"interaction_modal.on_another_server": "Na jiném serveru",
|
"interaction_modal.on_another_server": "Na jiném serveru",
|
||||||
"interaction_modal.on_this_server": "Na tomto serveru",
|
"interaction_modal.on_this_server": "Na tomto serveru",
|
||||||
|
"interaction_modal.title": "Chcete-li pokračovat, přihlaste se",
|
||||||
"interaction_modal.username_prompt": "např. {example}",
|
"interaction_modal.username_prompt": "např. {example}",
|
||||||
"intervals.full.days": "{number, plural, one {# den} few {# dny} many {# dní} other {# dní}}",
|
"intervals.full.days": "{number, plural, one {# den} few {# dny} many {# dní} other {# dní}}",
|
||||||
"intervals.full.hours": "{number, plural, one {# hodina} few {# hodiny} many {# hodin} other {# hodin}}",
|
"intervals.full.hours": "{number, plural, one {# hodina} few {# hodiny} many {# hodin} other {# hodin}}",
|
||||||
@ -860,7 +862,7 @@
|
|||||||
"status.block": "Blokovat @{name}",
|
"status.block": "Blokovat @{name}",
|
||||||
"status.bookmark": "Přidat do záložek",
|
"status.bookmark": "Přidat do záložek",
|
||||||
"status.cancel_reblog_private": "Zrušit boostnutí",
|
"status.cancel_reblog_private": "Zrušit boostnutí",
|
||||||
"status.cannot_quote": "Citování je na tomo příspěvku zakázáno",
|
"status.cannot_quote": "Nemáte oprávnění citovat tento příspěvek",
|
||||||
"status.cannot_reblog": "Tento příspěvek nemůže být boostnutý",
|
"status.cannot_reblog": "Tento příspěvek nemůže být boostnutý",
|
||||||
"status.context.load_new_replies": "K dispozici jsou nové odpovědi",
|
"status.context.load_new_replies": "K dispozici jsou nové odpovědi",
|
||||||
"status.context.loading": "Hledání dalších odpovědí",
|
"status.context.loading": "Hledání dalších odpovědí",
|
||||||
|
|||||||
@ -860,7 +860,6 @@
|
|||||||
"status.block": "Rhwystro @{name}",
|
"status.block": "Rhwystro @{name}",
|
||||||
"status.bookmark": "Nod tudalen",
|
"status.bookmark": "Nod tudalen",
|
||||||
"status.cancel_reblog_private": "Dadhybu",
|
"status.cancel_reblog_private": "Dadhybu",
|
||||||
"status.cannot_quote": "Mae dyfyniadau wedi'u hanalluogi ar y postiad hwn",
|
|
||||||
"status.cannot_reblog": "Does dim modd hybu'r postiad hwn",
|
"status.cannot_reblog": "Does dim modd hybu'r postiad hwn",
|
||||||
"status.context.load_new_replies": "Mae atebion newydd ar gael",
|
"status.context.load_new_replies": "Mae atebion newydd ar gael",
|
||||||
"status.context.loading": "Yn chwilio am fwy o atebion",
|
"status.context.loading": "Yn chwilio am fwy o atebion",
|
||||||
|
|||||||
@ -862,7 +862,7 @@
|
|||||||
"status.block": "Blokér @{name}",
|
"status.block": "Blokér @{name}",
|
||||||
"status.bookmark": "Bogmærk",
|
"status.bookmark": "Bogmærk",
|
||||||
"status.cancel_reblog_private": "Fjern fremhævelse",
|
"status.cancel_reblog_private": "Fjern fremhævelse",
|
||||||
"status.cannot_quote": "Citater er deaktiveret på dette indlæg",
|
"status.cannot_quote": "Du har ikke tilladelse til at citere dette indlæg",
|
||||||
"status.cannot_reblog": "Dette indlæg kan ikke fremhæves",
|
"status.cannot_reblog": "Dette indlæg kan ikke fremhæves",
|
||||||
"status.context.load_new_replies": "Nye svar tilgængelige",
|
"status.context.load_new_replies": "Nye svar tilgængelige",
|
||||||
"status.context.loading": "Tjekker for flere svar",
|
"status.context.loading": "Tjekker for flere svar",
|
||||||
|
|||||||
@ -862,7 +862,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": "Zitieren dieses Beitrags ist deaktiviert",
|
"status.cannot_quote": "Dir ist es nicht gestattet, diesen Beitrag zu zitieren",
|
||||||
"status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden",
|
"status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden",
|
||||||
"status.context.load_new_replies": "Neue Antworten verfügbar",
|
"status.context.load_new_replies": "Neue Antworten verfügbar",
|
||||||
"status.context.loading": "Weitere Antworten werden abgerufen",
|
"status.context.loading": "Weitere Antworten werden abgerufen",
|
||||||
|
|||||||
@ -860,7 +860,6 @@
|
|||||||
"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.load_new_replies": "Νέες απαντήσεις διαθέσιμες",
|
"status.context.load_new_replies": "Νέες απαντήσεις διαθέσιμες",
|
||||||
"status.context.loading": "Γίνεται έλεγχος για περισσότερες απαντήσεις",
|
"status.context.loading": "Γίνεται έλεγχος για περισσότερες απαντήσεις",
|
||||||
|
|||||||
@ -842,7 +842,6 @@
|
|||||||
"status.block": "Bloki @{name}",
|
"status.block": "Bloki @{name}",
|
||||||
"status.bookmark": "Aldoni al la legosignoj",
|
"status.bookmark": "Aldoni al la legosignoj",
|
||||||
"status.cancel_reblog_private": "Ne plu diskonigi",
|
"status.cancel_reblog_private": "Ne plu diskonigi",
|
||||||
"status.cannot_quote": "Citaĵoj estas malebligitaj en ĉi tiu afiŝo",
|
|
||||||
"status.cannot_reblog": "Ĉi tiun afiŝon ne eblas diskonigi",
|
"status.cannot_reblog": "Ĉi tiun afiŝon ne eblas diskonigi",
|
||||||
"status.context.load_new_replies": "Disponeblaj novaj respondoj",
|
"status.context.load_new_replies": "Disponeblaj novaj respondoj",
|
||||||
"status.context.loading": "Serĉante pliajn respondojn",
|
"status.context.loading": "Serĉante pliajn respondojn",
|
||||||
|
|||||||
@ -862,7 +862,7 @@
|
|||||||
"status.block": "Bloquear a @{name}",
|
"status.block": "Bloquear a @{name}",
|
||||||
"status.bookmark": "Marcar",
|
"status.bookmark": "Marcar",
|
||||||
"status.cancel_reblog_private": "Quitar adhesión",
|
"status.cancel_reblog_private": "Quitar adhesión",
|
||||||
"status.cannot_quote": "Las citas están deshabilitadas en este mensaje",
|
"status.cannot_quote": "No te es permitido citar este mensaje",
|
||||||
"status.cannot_reblog": "No se puede adherir a este mensaje",
|
"status.cannot_reblog": "No se puede adherir a este mensaje",
|
||||||
"status.context.load_new_replies": "Hay nuevas respuestas",
|
"status.context.load_new_replies": "Hay nuevas respuestas",
|
||||||
"status.context.loading": "Buscando más respuestas",
|
"status.context.loading": "Buscando más respuestas",
|
||||||
|
|||||||
@ -862,7 +862,7 @@
|
|||||||
"status.block": "Bloquear a @{name}",
|
"status.block": "Bloquear a @{name}",
|
||||||
"status.bookmark": "Añadir marcador",
|
"status.bookmark": "Añadir marcador",
|
||||||
"status.cancel_reblog_private": "Deshacer impulso",
|
"status.cancel_reblog_private": "Deshacer impulso",
|
||||||
"status.cannot_quote": "Las citas están desactivadas en esta publicación",
|
"status.cannot_quote": "No está permitido citar esta publicación",
|
||||||
"status.cannot_reblog": "Esta publicación no puede ser impulsada",
|
"status.cannot_reblog": "Esta publicación no puede ser impulsada",
|
||||||
"status.context.load_new_replies": "Nuevas respuestas disponibles",
|
"status.context.load_new_replies": "Nuevas respuestas disponibles",
|
||||||
"status.context.loading": "Comprobando si hay más respuestas",
|
"status.context.loading": "Comprobando si hay más respuestas",
|
||||||
|
|||||||
@ -862,7 +862,7 @@
|
|||||||
"status.block": "Bloquear a @{name}",
|
"status.block": "Bloquear a @{name}",
|
||||||
"status.bookmark": "Añadir marcador",
|
"status.bookmark": "Añadir marcador",
|
||||||
"status.cancel_reblog_private": "Deshacer impulso",
|
"status.cancel_reblog_private": "Deshacer impulso",
|
||||||
"status.cannot_quote": "Las citas están desactivadas en esta publicación",
|
"status.cannot_quote": "No está permitido citar esta publicación",
|
||||||
"status.cannot_reblog": "Esta publicación no se puede impulsar",
|
"status.cannot_reblog": "Esta publicación no se puede impulsar",
|
||||||
"status.context.load_new_replies": "Hay nuevas respuestas",
|
"status.context.load_new_replies": "Hay nuevas respuestas",
|
||||||
"status.context.loading": "Buscando más respuestas",
|
"status.context.loading": "Buscando más respuestas",
|
||||||
|
|||||||
@ -860,7 +860,6 @@
|
|||||||
"status.block": "Blokeeri @{name}",
|
"status.block": "Blokeeri @{name}",
|
||||||
"status.bookmark": "Järjehoidja",
|
"status.bookmark": "Järjehoidja",
|
||||||
"status.cancel_reblog_private": "Lõpeta jagamine",
|
"status.cancel_reblog_private": "Lõpeta jagamine",
|
||||||
"status.cannot_quote": "Selle postituse tsiteerimine pole lubatud",
|
|
||||||
"status.cannot_reblog": "Seda postitust ei saa jagada",
|
"status.cannot_reblog": "Seda postitust ei saa jagada",
|
||||||
"status.context.load_new_replies": "Leidub uusi vastuseid",
|
"status.context.load_new_replies": "Leidub uusi vastuseid",
|
||||||
"status.context.loading": "Kontrollin täiendavate vastuste olemasolu",
|
"status.context.loading": "Kontrollin täiendavate vastuste olemasolu",
|
||||||
|
|||||||
@ -841,7 +841,6 @@
|
|||||||
"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.load_new_replies": "پاسخهای جدیدی موجودند",
|
"status.context.load_new_replies": "پاسخهای جدیدی موجودند",
|
||||||
"status.context.loading": "بررسی کردن برای پاسخهای بیشتر",
|
"status.context.loading": "بررسی کردن برای پاسخهای بیشتر",
|
||||||
|
|||||||
@ -862,7 +862,7 @@
|
|||||||
"status.block": "Estä @{name}",
|
"status.block": "Estä @{name}",
|
||||||
"status.bookmark": "Lisää kirjanmerkki",
|
"status.bookmark": "Lisää kirjanmerkki",
|
||||||
"status.cancel_reblog_private": "Peru tehostus",
|
"status.cancel_reblog_private": "Peru tehostus",
|
||||||
"status.cannot_quote": "Tämän julkaisun lainaus on poistettu käytöstä",
|
"status.cannot_quote": "Sinulla ei ole oikeutta lainata tätä julkaisua",
|
||||||
"status.cannot_reblog": "Tätä julkaisua ei voi tehostaa",
|
"status.cannot_reblog": "Tätä julkaisua ei voi tehostaa",
|
||||||
"status.context.load_new_replies": "Uusia vastauksia saatavilla",
|
"status.context.load_new_replies": "Uusia vastauksia saatavilla",
|
||||||
"status.context.loading": "Tarkistetaan lisävastauksia",
|
"status.context.loading": "Tarkistetaan lisävastauksia",
|
||||||
|
|||||||
@ -862,7 +862,7 @@
|
|||||||
"status.block": "Blokera @{name}",
|
"status.block": "Blokera @{name}",
|
||||||
"status.bookmark": "Goym",
|
"status.bookmark": "Goym",
|
||||||
"status.cancel_reblog_private": "Strika stimbran",
|
"status.cancel_reblog_private": "Strika stimbran",
|
||||||
"status.cannot_quote": "Tað er ikki loyvt at sitera hendan postin",
|
"status.cannot_quote": "Tú hevur ikki loyvi at sitera hendan postin",
|
||||||
"status.cannot_reblog": "Tað ber ikki til at stimbra hendan postin",
|
"status.cannot_reblog": "Tað ber ikki til at stimbra hendan postin",
|
||||||
"status.context.load_new_replies": "Nýggj svar tøk",
|
"status.context.load_new_replies": "Nýggj svar tøk",
|
||||||
"status.context.loading": "Kanni um tað eru fleiri svar",
|
"status.context.loading": "Kanni um tað eru fleiri svar",
|
||||||
|
|||||||
@ -846,7 +846,6 @@
|
|||||||
"status.block": "Bloquer @{name}",
|
"status.block": "Bloquer @{name}",
|
||||||
"status.bookmark": "Ajouter aux signets",
|
"status.bookmark": "Ajouter aux signets",
|
||||||
"status.cancel_reblog_private": "Débooster",
|
"status.cancel_reblog_private": "Débooster",
|
||||||
"status.cannot_quote": "Les citations sont désactivées sur cette publication",
|
|
||||||
"status.cannot_reblog": "Cette publication ne peut pas être boostée",
|
"status.cannot_reblog": "Cette publication ne peut pas être boostée",
|
||||||
"status.context.load_new_replies": "Nouvelles réponses disponibles",
|
"status.context.load_new_replies": "Nouvelles réponses disponibles",
|
||||||
"status.continued_thread": "Suite du fil",
|
"status.continued_thread": "Suite du fil",
|
||||||
|
|||||||
@ -846,7 +846,6 @@
|
|||||||
"status.block": "Bloquer @{name}",
|
"status.block": "Bloquer @{name}",
|
||||||
"status.bookmark": "Ajouter aux marque-pages",
|
"status.bookmark": "Ajouter aux marque-pages",
|
||||||
"status.cancel_reblog_private": "Annuler le partage",
|
"status.cancel_reblog_private": "Annuler le partage",
|
||||||
"status.cannot_quote": "Les citations sont désactivées sur cette publication",
|
|
||||||
"status.cannot_reblog": "Ce message ne peut pas être partagé",
|
"status.cannot_reblog": "Ce message ne peut pas être partagé",
|
||||||
"status.context.load_new_replies": "Nouvelles réponses disponibles",
|
"status.context.load_new_replies": "Nouvelles réponses disponibles",
|
||||||
"status.continued_thread": "Suite du fil",
|
"status.continued_thread": "Suite du fil",
|
||||||
|
|||||||
@ -453,10 +453,12 @@
|
|||||||
"ignore_notifications_modal.private_mentions_title": "An dtugann tú aird ar fhógraí ó Luaintí Príobháideacha gan iarraidh?",
|
"ignore_notifications_modal.private_mentions_title": "An dtugann tú aird ar fhógraí ó Luaintí Príobháideacha gan iarraidh?",
|
||||||
"info_button.label": "Cabhrú",
|
"info_button.label": "Cabhrú",
|
||||||
"info_button.what_is_alt_text": "<h1>Cad is téacs altach ann?</h1> <p>Soláthraíonn téacs Alt cur síos ar íomhánna do dhaoine le lagú radhairc, naisc íseal-bandaleithead, nó daoine atá ag lorg comhthéacs breise.</p> <p>Is féidir leat inrochtaineacht agus tuiscint a fheabhsú do chách trí théacs alt soiléir, gonta, oibiachtúil a scríobh.</p> <ul> <li>Glac gnéithe tábhachtacha</li> <li>Déan achoimre ar théacs in íomhánna</li> <li>Úsáid struchtúr abairtí rialta</li> li> <li>Seachain faisnéis iomarcach</li> <li>Fócas ar threochtaí agus ar phríomhthorthaí i bhfíseanna casta (amhail léaráidí nó léarscáileanna)</li> </ul>",
|
"info_button.what_is_alt_text": "<h1>Cad is téacs altach ann?</h1> <p>Soláthraíonn téacs Alt cur síos ar íomhánna do dhaoine le lagú radhairc, naisc íseal-bandaleithead, nó daoine atá ag lorg comhthéacs breise.</p> <p>Is féidir leat inrochtaineacht agus tuiscint a fheabhsú do chách trí théacs alt soiléir, gonta, oibiachtúil a scríobh.</p> <ul> <li>Glac gnéithe tábhachtacha</li> <li>Déan achoimre ar théacs in íomhánna</li> <li>Úsáid struchtúr abairtí rialta</li> li> <li>Seachain faisnéis iomarcach</li> <li>Fócas ar threochtaí agus ar phríomhthorthaí i bhfíseanna casta (amhail léaráidí nó léarscáileanna)</li> </ul>",
|
||||||
|
"interaction_modal.action": "Chun idirghníomhú le post {name}, ní mór duit síniú isteach i do chuntas ar cibé freastalaí Mastodon a úsáideann tú.",
|
||||||
"interaction_modal.go": "Téigh",
|
"interaction_modal.go": "Téigh",
|
||||||
"interaction_modal.no_account_yet": "Níl cuntas agat fós?",
|
"interaction_modal.no_account_yet": "Níl cuntas agat fós?",
|
||||||
"interaction_modal.on_another_server": "Ar freastalaí eile",
|
"interaction_modal.on_another_server": "Ar freastalaí eile",
|
||||||
"interaction_modal.on_this_server": "Ar an freastalaí seo",
|
"interaction_modal.on_this_server": "Ar an freastalaí seo",
|
||||||
|
"interaction_modal.title": "Sínigh isteach le leanúint ar aghaidh",
|
||||||
"interaction_modal.username_prompt": "M.sh. {example}",
|
"interaction_modal.username_prompt": "M.sh. {example}",
|
||||||
"intervals.full.days": "{number, plural, one {# lá} other {# lá}}",
|
"intervals.full.days": "{number, plural, one {# lá} other {# lá}}",
|
||||||
"intervals.full.hours": "{number, plural, one {# uair} other {# uair}}",
|
"intervals.full.hours": "{number, plural, one {# uair} other {# uair}}",
|
||||||
@ -860,7 +862,6 @@
|
|||||||
"status.block": "Bac @{name}",
|
"status.block": "Bac @{name}",
|
||||||
"status.bookmark": "Leabharmharcanna",
|
"status.bookmark": "Leabharmharcanna",
|
||||||
"status.cancel_reblog_private": "Dímhol",
|
"status.cancel_reblog_private": "Dímhol",
|
||||||
"status.cannot_quote": "Tá luachana díchumasaithe ar an bpost seo",
|
|
||||||
"status.cannot_reblog": "Ní féidir an phostáil seo a mholadh",
|
"status.cannot_reblog": "Ní féidir an phostáil seo a mholadh",
|
||||||
"status.context.load_new_replies": "Freagraí nua ar fáil",
|
"status.context.load_new_replies": "Freagraí nua ar fáil",
|
||||||
"status.context.loading": "Ag seiceáil le haghaidh tuilleadh freagraí",
|
"status.context.loading": "Ag seiceáil le haghaidh tuilleadh freagraí",
|
||||||
|
|||||||
@ -239,6 +239,8 @@
|
|||||||
"confirmations.missing_alt_text.secondary": "Postaich e co-dhiù",
|
"confirmations.missing_alt_text.secondary": "Postaich e co-dhiù",
|
||||||
"confirmations.missing_alt_text.title": "A bheil thu airson roghainn teacsa a chur ris?",
|
"confirmations.missing_alt_text.title": "A bheil thu airson roghainn teacsa a chur ris?",
|
||||||
"confirmations.mute.confirm": "Mùch",
|
"confirmations.mute.confirm": "Mùch",
|
||||||
|
"confirmations.quiet_post_quote_info.dismiss": "Na cuiribh seo ’nam chuimhne a-rithist",
|
||||||
|
"confirmations.quiet_post_quote_info.got_it": "Tha mi agaibh",
|
||||||
"confirmations.redraft.confirm": "Sguab às ⁊ dèan dreachd ùr",
|
"confirmations.redraft.confirm": "Sguab às ⁊ dèan dreachd ùr",
|
||||||
"confirmations.redraft.message": "A bheil thu cinnteach gu bheil thu airson am post seo a sguabadh às agus dreachd ùr a thòiseachadh? Caillidh tu gach annsachd is brosnachadh air agus thèid freagairtean dhan phost thùsail ’nan dìlleachdanan.",
|
"confirmations.redraft.message": "A bheil thu cinnteach gu bheil thu airson am post seo a sguabadh às agus dreachd ùr a thòiseachadh? Caillidh tu gach annsachd is brosnachadh air agus thèid freagairtean dhan phost thùsail ’nan dìlleachdanan.",
|
||||||
"confirmations.redraft.title": "A bheil thu airson am post a sguabadh às ⁊ dreachd ùr a dhèanamh dheth?",
|
"confirmations.redraft.title": "A bheil thu airson am post a sguabadh às ⁊ dreachd ùr a dhèanamh dheth?",
|
||||||
@ -477,6 +479,7 @@
|
|||||||
"keyboard_shortcuts.open_media": "Fosgail meadhan",
|
"keyboard_shortcuts.open_media": "Fosgail meadhan",
|
||||||
"keyboard_shortcuts.pinned": "Fosgail liosta nam postaichean prìnichte",
|
"keyboard_shortcuts.pinned": "Fosgail liosta nam postaichean prìnichte",
|
||||||
"keyboard_shortcuts.profile": "Fosgail pròifil an ùghdair",
|
"keyboard_shortcuts.profile": "Fosgail pròifil an ùghdair",
|
||||||
|
"keyboard_shortcuts.quote": "Luaidh am post",
|
||||||
"keyboard_shortcuts.reply": "Freagair do phost",
|
"keyboard_shortcuts.reply": "Freagair do phost",
|
||||||
"keyboard_shortcuts.requests": "Fosgail liosta nan iarrtasan leantainn",
|
"keyboard_shortcuts.requests": "Fosgail liosta nan iarrtasan leantainn",
|
||||||
"keyboard_shortcuts.search": "Cuir am fòcas air a’ bhàr-luirg",
|
"keyboard_shortcuts.search": "Cuir am fòcas air a’ bhàr-luirg",
|
||||||
@ -588,6 +591,7 @@
|
|||||||
"notification.label.mention": "Iomradh",
|
"notification.label.mention": "Iomradh",
|
||||||
"notification.label.private_mention": "Iomradh prìobhaideach",
|
"notification.label.private_mention": "Iomradh prìobhaideach",
|
||||||
"notification.label.private_reply": "Freagairt phrìobhaideach",
|
"notification.label.private_reply": "Freagairt phrìobhaideach",
|
||||||
|
"notification.label.quote": "Luaidh {name} am post agad",
|
||||||
"notification.label.reply": "Freagairt",
|
"notification.label.reply": "Freagairt",
|
||||||
"notification.mention": "Iomradh",
|
"notification.mention": "Iomradh",
|
||||||
"notification.mentioned_you": "Thug {name} iomradh ort",
|
"notification.mentioned_you": "Thug {name} iomradh ort",
|
||||||
@ -602,6 +606,7 @@
|
|||||||
"notification.moderation_warning.action_suspend": "Chaidh an cunntas agad a chur à rèim.",
|
"notification.moderation_warning.action_suspend": "Chaidh an cunntas agad a chur à rèim.",
|
||||||
"notification.own_poll": "Thàinig an cunntas-bheachd agad gu crìoch",
|
"notification.own_poll": "Thàinig an cunntas-bheachd agad gu crìoch",
|
||||||
"notification.poll": "Thàinig cunntas-bheachd sa bhòt thu gu crìoch",
|
"notification.poll": "Thàinig cunntas-bheachd sa bhòt thu gu crìoch",
|
||||||
|
"notification.quoted_update": "Dheasaich {name} post a luaidh thu",
|
||||||
"notification.reblog": "Bhrosnaich {name} am post agad",
|
"notification.reblog": "Bhrosnaich {name} am post agad",
|
||||||
"notification.reblog.name_and_others_with_link": "Bhrosnaich {name} ’s <a>{count, plural, one {# eile} two {# eile} few {# eile} other {# eile}}</a> am post agad",
|
"notification.reblog.name_and_others_with_link": "Bhrosnaich {name} ’s <a>{count, plural, one {# eile} two {# eile} few {# eile} other {# eile}}</a> am post agad",
|
||||||
"notification.relationships_severance_event": "Chaill thu dàimhean le {name}",
|
"notification.relationships_severance_event": "Chaill thu dàimhean le {name}",
|
||||||
@ -645,6 +650,7 @@
|
|||||||
"notifications.column_settings.mention": "Iomraidhean:",
|
"notifications.column_settings.mention": "Iomraidhean:",
|
||||||
"notifications.column_settings.poll": "Toraidhean cunntais-bheachd:",
|
"notifications.column_settings.poll": "Toraidhean cunntais-bheachd:",
|
||||||
"notifications.column_settings.push": "Brathan putaidh",
|
"notifications.column_settings.push": "Brathan putaidh",
|
||||||
|
"notifications.column_settings.quote": "Luaidhean:",
|
||||||
"notifications.column_settings.reblog": "Brosnachaidhean:",
|
"notifications.column_settings.reblog": "Brosnachaidhean:",
|
||||||
"notifications.column_settings.show": "Seall sa cholbh",
|
"notifications.column_settings.show": "Seall sa cholbh",
|
||||||
"notifications.column_settings.sound": "Cluich fuaim",
|
"notifications.column_settings.sound": "Cluich fuaim",
|
||||||
@ -720,10 +726,15 @@
|
|||||||
"privacy.private.short": "Luchd-leantainn",
|
"privacy.private.short": "Luchd-leantainn",
|
||||||
"privacy.public.long": "Duine sam bith taobh a-staigh no a-muigh Mhastodon",
|
"privacy.public.long": "Duine sam bith taobh a-staigh no a-muigh Mhastodon",
|
||||||
"privacy.public.short": "Poblach",
|
"privacy.public.short": "Poblach",
|
||||||
|
"privacy.quote.anyone": "{visibility}, faodaidh neach sam bith a luaidh",
|
||||||
|
"privacy.quote.disabled": "{visibility}, luaidhean à comas",
|
||||||
|
"privacy.quote.limited": "{visibility}, luaidhean cuingichte",
|
||||||
"privacy.unlisted.additional": "Tha seo coltach ris an fhaicsinneachd phoblach ach cha nochd am post air loidhnichean-ama an t-saoghail phoblaich, nan tagaichean hais no an rùrachaidh no ann an toraidhean luirg Mhastodon fiù ’s ma thug thu ro-aonta airson sin seachad.",
|
"privacy.unlisted.additional": "Tha seo coltach ris an fhaicsinneachd phoblach ach cha nochd am post air loidhnichean-ama an t-saoghail phoblaich, nan tagaichean hais no an rùrachaidh no ann an toraidhean luirg Mhastodon fiù ’s ma thug thu ro-aonta airson sin seachad.",
|
||||||
"privacy.unlisted.short": "Poblach ach sàmhach",
|
"privacy.unlisted.short": "Poblach ach sàmhach",
|
||||||
"privacy_policy.last_updated": "An t-ùrachadh mu dheireadh {date}",
|
"privacy_policy.last_updated": "An t-ùrachadh mu dheireadh {date}",
|
||||||
"privacy_policy.title": "Poileasaidh prìobhaideachd",
|
"privacy_policy.title": "Poileasaidh prìobhaideachd",
|
||||||
|
"quote_error.quote": "Chan eil taic ach ri aon luaidh aig an aon àm.",
|
||||||
|
"quote_error.unauthorized": "Chan fhaod thu am post seo a luaidh.",
|
||||||
"recommended": "Molta",
|
"recommended": "Molta",
|
||||||
"refresh": "Ath-nuadhaich",
|
"refresh": "Ath-nuadhaich",
|
||||||
"regeneration_indicator.please_stand_by": "Fuirich ort.",
|
"regeneration_indicator.please_stand_by": "Fuirich ort.",
|
||||||
@ -830,9 +841,11 @@
|
|||||||
"status.admin_account": "Fosgail eadar-aghaidh na maorsainneachd dha @{name}",
|
"status.admin_account": "Fosgail eadar-aghaidh na maorsainneachd dha @{name}",
|
||||||
"status.admin_domain": "Fosgail eadar-aghaidh na maorsainneachd dha {domain}",
|
"status.admin_domain": "Fosgail eadar-aghaidh na maorsainneachd dha {domain}",
|
||||||
"status.admin_status": "Fosgail am post seo ann an eadar-aghaidh na maorsainneachd",
|
"status.admin_status": "Fosgail am post seo ann an eadar-aghaidh na maorsainneachd",
|
||||||
|
"status.all_disabled": "Tha brosnachaidhean ’s luaidhean à comas",
|
||||||
"status.block": "Bac @{name}",
|
"status.block": "Bac @{name}",
|
||||||
"status.bookmark": "Cuir ris na comharran-lìn",
|
"status.bookmark": "Cuir ris na comharran-lìn",
|
||||||
"status.cancel_reblog_private": "Na brosnaich tuilleadh",
|
"status.cancel_reblog_private": "Na brosnaich tuilleadh",
|
||||||
|
"status.cannot_quote": "Chan fhaod thu am post seo a luaidh",
|
||||||
"status.cannot_reblog": "Cha ghabh am post seo brosnachadh",
|
"status.cannot_reblog": "Cha ghabh am post seo brosnachadh",
|
||||||
"status.continued_thread": "Pàirt de shnàithlean",
|
"status.continued_thread": "Pàirt de shnàithlean",
|
||||||
"status.copy": "Dèan lethbhreac dhen cheangal dhan phost",
|
"status.copy": "Dèan lethbhreac dhen cheangal dhan phost",
|
||||||
@ -859,6 +872,8 @@
|
|||||||
"status.mute_conversation": "Mùch an còmhradh",
|
"status.mute_conversation": "Mùch an còmhradh",
|
||||||
"status.open": "Leudaich am post seo",
|
"status.open": "Leudaich am post seo",
|
||||||
"status.pin": "Prìnich ris a’ phròifil",
|
"status.pin": "Prìnich ris a’ phròifil",
|
||||||
|
"status.quote": "Luaidh",
|
||||||
|
"status.quote.cancel": "Sguir dhen luaidh",
|
||||||
"status.quote_error.filtered": "Falaichte le criathrag a th’ agad",
|
"status.quote_error.filtered": "Falaichte le criathrag a th’ agad",
|
||||||
"status.read_more": "Leugh an còrr",
|
"status.read_more": "Leugh an còrr",
|
||||||
"status.reblog": "Brosnaich",
|
"status.reblog": "Brosnaich",
|
||||||
|
|||||||
@ -453,10 +453,12 @@
|
|||||||
"ignore_notifications_modal.private_mentions_title": "Ignorar notificacións de Mencións Privadas non solicitadas?",
|
"ignore_notifications_modal.private_mentions_title": "Ignorar notificacións de Mencións Privadas non solicitadas?",
|
||||||
"info_button.label": "Axuda",
|
"info_button.label": "Axuda",
|
||||||
"info_button.what_is_alt_text": "<h1>Que é o Texto Alternativo?</h1> <p>O Text Alt proporciona a descrición das imaxes para as persoas con deficiencias visuais, conexións a internet de baixa calidade ou para engadir contexto ás mesmas.</p> <p>Podes mellorar a accesibilidade e a comprensión da publicación ao escribir un texto alternativo claro, conciso e obxectivo.</p> <ul> <li>Identifica os elementos importantes</li> <li>Inclúe o texto que apareza nas imaxes</li> <li>Utiliza sintaxe estándar nas frases</li> <li>Evita información redundante</li> <li>Céntrate nos elementos principais cando sexan imaxes complexas (como diagramas ou mapas)</li> </ul>",
|
"info_button.what_is_alt_text": "<h1>Que é o Texto Alternativo?</h1> <p>O Text Alt proporciona a descrición das imaxes para as persoas con deficiencias visuais, conexións a internet de baixa calidade ou para engadir contexto ás mesmas.</p> <p>Podes mellorar a accesibilidade e a comprensión da publicación ao escribir un texto alternativo claro, conciso e obxectivo.</p> <ul> <li>Identifica os elementos importantes</li> <li>Inclúe o texto que apareza nas imaxes</li> <li>Utiliza sintaxe estándar nas frases</li> <li>Evita información redundante</li> <li>Céntrate nos elementos principais cando sexan imaxes complexas (como diagramas ou mapas)</li> </ul>",
|
||||||
|
"interaction_modal.action": "Para interactuar coa publicación de {name} debes iniciar sesión no servidor Mastodon que utilices.",
|
||||||
"interaction_modal.go": "Ir",
|
"interaction_modal.go": "Ir",
|
||||||
"interaction_modal.no_account_yet": "Aínda non tes unha conta?",
|
"interaction_modal.no_account_yet": "Aínda non tes unha conta?",
|
||||||
"interaction_modal.on_another_server": "Nun servidor diferente",
|
"interaction_modal.on_another_server": "Nun servidor diferente",
|
||||||
"interaction_modal.on_this_server": "Neste servidor",
|
"interaction_modal.on_this_server": "Neste servidor",
|
||||||
|
"interaction_modal.title": "Inicia sesión para continuar",
|
||||||
"interaction_modal.username_prompt": "Ex. {example}",
|
"interaction_modal.username_prompt": "Ex. {example}",
|
||||||
"intervals.full.days": "{number, plural,one {# día} other {# días}}",
|
"intervals.full.days": "{number, plural,one {# día} other {# días}}",
|
||||||
"intervals.full.hours": "{number, plural, one {# hora} other {# horas}}",
|
"intervals.full.hours": "{number, plural, one {# hora} other {# horas}}",
|
||||||
@ -860,7 +862,6 @@
|
|||||||
"status.block": "Bloquear a @{name}",
|
"status.block": "Bloquear a @{name}",
|
||||||
"status.bookmark": "Marcar",
|
"status.bookmark": "Marcar",
|
||||||
"status.cancel_reblog_private": "Desfacer compartido",
|
"status.cancel_reblog_private": "Desfacer compartido",
|
||||||
"status.cannot_quote": "Non se permite citar esta publicación",
|
|
||||||
"status.cannot_reblog": "Esta publicación non pode ser promovida",
|
"status.cannot_reblog": "Esta publicación non pode ser promovida",
|
||||||
"status.context.load_new_replies": "Non hai respostas dispoñibles",
|
"status.context.load_new_replies": "Non hai respostas dispoñibles",
|
||||||
"status.context.loading": "Mirando se hai máis respostas",
|
"status.context.loading": "Mirando se hai máis respostas",
|
||||||
|
|||||||
@ -860,7 +860,6 @@
|
|||||||
"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.load_new_replies": "הגיעו תגובות חדשות",
|
"status.context.load_new_replies": "הגיעו תגובות חדשות",
|
||||||
"status.context.loading": "מחפש תגובות חדשות",
|
"status.context.loading": "מחפש תגובות חדשות",
|
||||||
|
|||||||
@ -860,7 +860,6 @@
|
|||||||
"status.block": "@{name} letiltása",
|
"status.block": "@{name} letiltása",
|
||||||
"status.bookmark": "Könyvjelzőzés",
|
"status.bookmark": "Könyvjelzőzés",
|
||||||
"status.cancel_reblog_private": "Megtolás visszavonása",
|
"status.cancel_reblog_private": "Megtolás visszavonása",
|
||||||
"status.cannot_quote": "Az idézés ki van kapcsolva ennél a bejegyzésnél",
|
|
||||||
"status.cannot_reblog": "Ezt a bejegyzést nem lehet megtolni",
|
"status.cannot_reblog": "Ezt a bejegyzést nem lehet megtolni",
|
||||||
"status.context.load_new_replies": "Új válaszok érhetőek el",
|
"status.context.load_new_replies": "Új válaszok érhetőek el",
|
||||||
"status.context.loading": "További válaszok keresése",
|
"status.context.loading": "További válaszok keresése",
|
||||||
|
|||||||
@ -860,7 +860,6 @@
|
|||||||
"status.block": "Blocar @{name}",
|
"status.block": "Blocar @{name}",
|
||||||
"status.bookmark": "Adder al marcapaginas",
|
"status.bookmark": "Adder al marcapaginas",
|
||||||
"status.cancel_reblog_private": "Disfacer impulso",
|
"status.cancel_reblog_private": "Disfacer impulso",
|
||||||
"status.cannot_quote": "Le citationes es disactivate pro iste message",
|
|
||||||
"status.cannot_reblog": "Iste message non pote esser impulsate",
|
"status.cannot_reblog": "Iste message non pote esser impulsate",
|
||||||
"status.context.load_new_replies": "Nove responsas disponibile",
|
"status.context.load_new_replies": "Nove responsas disponibile",
|
||||||
"status.context.loading": "Cercante plus responsas",
|
"status.context.loading": "Cercante plus responsas",
|
||||||
|
|||||||
@ -862,7 +862,7 @@
|
|||||||
"status.block": "Útiloka @{name}",
|
"status.block": "Útiloka @{name}",
|
||||||
"status.bookmark": "Bókamerki",
|
"status.bookmark": "Bókamerki",
|
||||||
"status.cancel_reblog_private": "Taka úr endurbirtingu",
|
"status.cancel_reblog_private": "Taka úr endurbirtingu",
|
||||||
"status.cannot_quote": "Tilvitnanir eru gerðar óvirkar á þessari færslu",
|
"status.cannot_quote": "Þú hefur ekki heimild til að vitna í þessa færslu",
|
||||||
"status.cannot_reblog": "Þessa færslu er ekki hægt að endurbirta",
|
"status.cannot_reblog": "Þessa færslu er ekki hægt að endurbirta",
|
||||||
"status.context.load_new_replies": "Ný svör hafa borist",
|
"status.context.load_new_replies": "Ný svör hafa borist",
|
||||||
"status.context.loading": "Athuga með fleiri svör",
|
"status.context.loading": "Athuga með fleiri svör",
|
||||||
|
|||||||
@ -453,10 +453,12 @@
|
|||||||
"ignore_notifications_modal.private_mentions_title": "Ignorare le notifiche provenienti da menzioni private indesiderate?",
|
"ignore_notifications_modal.private_mentions_title": "Ignorare le notifiche provenienti da menzioni private indesiderate?",
|
||||||
"info_button.label": "Aiuto",
|
"info_button.label": "Aiuto",
|
||||||
"info_button.what_is_alt_text": "<h1>Cos'è il testo alternativo?</h1> <p>Il testo alternativo fornisce descrizioni delle immagini per le persone con disturbi della vista, connessioni a bassa larghezza di banda o per coloro che cercano un contesto aggiuntivo.</p> <p>È possibile migliorare l'accessibilità e la comprensione per tutti scrivendo un testo alt chiaro, conciso e obiettivo.</p> <ul> <li>Cattura elementi importanti</li> <li>Riassume il testo nelle immagini</li> <li>Usa la struttura delle frasi regolari</li> <li>Evita le informazioni ridondanti</li> <li>Concentrati sulle tendenze e i risultati chiave in immagini complesse (come diagrammi o mappe)</li> </ul>",
|
"info_button.what_is_alt_text": "<h1>Cos'è il testo alternativo?</h1> <p>Il testo alternativo fornisce descrizioni delle immagini per le persone con disturbi della vista, connessioni a bassa larghezza di banda o per coloro che cercano un contesto aggiuntivo.</p> <p>È possibile migliorare l'accessibilità e la comprensione per tutti scrivendo un testo alt chiaro, conciso e obiettivo.</p> <ul> <li>Cattura elementi importanti</li> <li>Riassume il testo nelle immagini</li> <li>Usa la struttura delle frasi regolari</li> <li>Evita le informazioni ridondanti</li> <li>Concentrati sulle tendenze e i risultati chiave in immagini complesse (come diagrammi o mappe)</li> </ul>",
|
||||||
|
"interaction_modal.action": "Per interagire con il post di {name}, devi accedere al tuo account su qualsiasi server Mastodon tu utilizzi.",
|
||||||
"interaction_modal.go": "Vai",
|
"interaction_modal.go": "Vai",
|
||||||
"interaction_modal.no_account_yet": "Non hai ancora un account?",
|
"interaction_modal.no_account_yet": "Non hai ancora un account?",
|
||||||
"interaction_modal.on_another_server": "Su un altro server",
|
"interaction_modal.on_another_server": "Su un altro server",
|
||||||
"interaction_modal.on_this_server": "Su questo server",
|
"interaction_modal.on_this_server": "Su questo server",
|
||||||
|
"interaction_modal.title": "Accedi per continuare",
|
||||||
"interaction_modal.username_prompt": "Es. {example}",
|
"interaction_modal.username_prompt": "Es. {example}",
|
||||||
"intervals.full.days": "{number, plural, one {# giorno} other {# giorni}}",
|
"intervals.full.days": "{number, plural, one {# giorno} other {# giorni}}",
|
||||||
"intervals.full.hours": "{number, plural, one {# ora} other {# ore}}",
|
"intervals.full.hours": "{number, plural, one {# ora} other {# ore}}",
|
||||||
@ -860,7 +862,7 @@
|
|||||||
"status.block": "Blocca @{name}",
|
"status.block": "Blocca @{name}",
|
||||||
"status.bookmark": "Aggiungi segnalibro",
|
"status.bookmark": "Aggiungi segnalibro",
|
||||||
"status.cancel_reblog_private": "Annulla reblog",
|
"status.cancel_reblog_private": "Annulla reblog",
|
||||||
"status.cannot_quote": "Le citazioni sono disabilitate su questo post",
|
"status.cannot_quote": "Non ti è consentito citare questo post",
|
||||||
"status.cannot_reblog": "Questo post non può essere condiviso",
|
"status.cannot_reblog": "Questo post non può essere condiviso",
|
||||||
"status.context.load_new_replies": "Nuove risposte disponibili",
|
"status.context.load_new_replies": "Nuove risposte disponibili",
|
||||||
"status.context.loading": "Controllo per altre risposte",
|
"status.context.loading": "Controllo per altre risposte",
|
||||||
|
|||||||
@ -860,7 +860,6 @@
|
|||||||
"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.load_new_replies": "새 답글 보기",
|
"status.context.load_new_replies": "새 답글 보기",
|
||||||
"status.context.loading": "추가 답글 확인중",
|
"status.context.loading": "추가 답글 확인중",
|
||||||
|
|||||||
@ -860,7 +860,6 @@
|
|||||||
"status.block": "封鎖 @{name}",
|
"status.block": "封鎖 @{name}",
|
||||||
"status.bookmark": "冊籤",
|
"status.bookmark": "冊籤",
|
||||||
"status.cancel_reblog_private": "取消轉送",
|
"status.cancel_reblog_private": "取消轉送",
|
||||||
"status.cannot_quote": "Tsit篇PO文停止使用引用功能ah",
|
|
||||||
"status.cannot_reblog": "Tsit篇PO文bē當轉送",
|
"status.cannot_reblog": "Tsit篇PO文bē當轉送",
|
||||||
"status.context.load_new_replies": "有新ê回應",
|
"status.context.load_new_replies": "有新ê回應",
|
||||||
"status.context.loading": "Leh檢查其他ê回應",
|
"status.context.loading": "Leh檢查其他ê回應",
|
||||||
|
|||||||
@ -453,10 +453,12 @@
|
|||||||
"ignore_notifications_modal.private_mentions_title": "Meldingen negeren van ongevraagde privéberichten?",
|
"ignore_notifications_modal.private_mentions_title": "Meldingen negeren van ongevraagde privéberichten?",
|
||||||
"info_button.label": "Help",
|
"info_button.label": "Help",
|
||||||
"info_button.what_is_alt_text": "<h1>Wat is alt-tekst?</h1> <p>Alt-tekst biedt beschrijvingen van afbeeldingen voor mensen met een visuele beperking, voor verbindingen met lage internetsnelheid of mensen die op zoek zijn naar extra context.</p> <p>Je kunt de toegankelijkheid en de begrijpelijkheid voor iedereen verbeteren door heldere, beknopte en objectieve alt-teksten te schrijven.</p> <ul> <li>Leg belangrijke elementen vast</li> <li>Tekst in afbeeldingen samenvatten</li> <li>Een eenvoudige zinsbouw gebruiken</li> <li>Overbodige informatie vermijden</li> <li>Voor complexe diagrammen of kaarten alleen op trends en belangrijke bevindingen focussen</li> </ul>",
|
"info_button.what_is_alt_text": "<h1>Wat is alt-tekst?</h1> <p>Alt-tekst biedt beschrijvingen van afbeeldingen voor mensen met een visuele beperking, voor verbindingen met lage internetsnelheid of mensen die op zoek zijn naar extra context.</p> <p>Je kunt de toegankelijkheid en de begrijpelijkheid voor iedereen verbeteren door heldere, beknopte en objectieve alt-teksten te schrijven.</p> <ul> <li>Leg belangrijke elementen vast</li> <li>Tekst in afbeeldingen samenvatten</li> <li>Een eenvoudige zinsbouw gebruiken</li> <li>Overbodige informatie vermijden</li> <li>Voor complexe diagrammen of kaarten alleen op trends en belangrijke bevindingen focussen</li> </ul>",
|
||||||
|
"interaction_modal.action": "Wanneer je met het bericht van {name} interactie wil hebben, moet je met jouw account op jouw eigen Mastodon-server inloggen.",
|
||||||
"interaction_modal.go": "Ga",
|
"interaction_modal.go": "Ga",
|
||||||
"interaction_modal.no_account_yet": "Heb je nog geen account?",
|
"interaction_modal.no_account_yet": "Heb je nog geen account?",
|
||||||
"interaction_modal.on_another_server": "Op een andere server",
|
"interaction_modal.on_another_server": "Op een andere server",
|
||||||
"interaction_modal.on_this_server": "Op deze server",
|
"interaction_modal.on_this_server": "Op deze server",
|
||||||
|
"interaction_modal.title": "Inloggen om door te gaan",
|
||||||
"interaction_modal.username_prompt": "Bijv. {example}",
|
"interaction_modal.username_prompt": "Bijv. {example}",
|
||||||
"intervals.full.days": "{number, plural, one {# dag} other {# dagen}}",
|
"intervals.full.days": "{number, plural, one {# dag} other {# dagen}}",
|
||||||
"intervals.full.hours": "{number, plural, one {# uur} other {# uur}}",
|
"intervals.full.hours": "{number, plural, one {# uur} other {# uur}}",
|
||||||
@ -860,7 +862,7 @@
|
|||||||
"status.block": "@{name} blokkeren",
|
"status.block": "@{name} blokkeren",
|
||||||
"status.bookmark": "Bladwijzer toevoegen",
|
"status.bookmark": "Bladwijzer toevoegen",
|
||||||
"status.cancel_reblog_private": "Niet langer boosten",
|
"status.cancel_reblog_private": "Niet langer boosten",
|
||||||
"status.cannot_quote": "Citaten zijn voor dit bericht uitgeschakeld",
|
"status.cannot_quote": "Je bent niet gemachtigd om dit bericht te citeren",
|
||||||
"status.cannot_reblog": "Dit bericht kan niet geboost worden",
|
"status.cannot_reblog": "Dit bericht kan niet geboost worden",
|
||||||
"status.context.load_new_replies": "Nieuwe reacties beschikbaar",
|
"status.context.load_new_replies": "Nieuwe reacties beschikbaar",
|
||||||
"status.context.loading": "Op nieuwe reacties aan het controleren",
|
"status.context.loading": "Op nieuwe reacties aan het controleren",
|
||||||
@ -895,7 +897,7 @@
|
|||||||
"status.quote_error.filtered": "Verborgen door een van je filters",
|
"status.quote_error.filtered": "Verborgen door een van je filters",
|
||||||
"status.quote_error.not_available": "Bericht niet beschikbaar",
|
"status.quote_error.not_available": "Bericht niet beschikbaar",
|
||||||
"status.quote_error.pending_approval": "Bericht in afwachting",
|
"status.quote_error.pending_approval": "Bericht in afwachting",
|
||||||
"status.quote_error.pending_approval_popout.body": "Op Mastodon kun je bepalen of iemand je kan citeren. Dit bericht is in afwachting van de goedkeuring van de oorspronkelijke auteur.",
|
"status.quote_error.pending_approval_popout.body": "Op Mastodon kun je bepalen of iemand je mag citeren. Dit bericht is in afwachting van de goedkeuring van de oorspronkelijke auteur.",
|
||||||
"status.quote_error.revoked": "Bericht verwijderd door auteur",
|
"status.quote_error.revoked": "Bericht verwijderd door auteur",
|
||||||
"status.quote_followers_only": "Alleen volgers mogen dit bericht citeren",
|
"status.quote_followers_only": "Alleen volgers mogen dit bericht citeren",
|
||||||
"status.quote_manual_review": "De auteur gaat het handmatig beoordelen",
|
"status.quote_manual_review": "De auteur gaat het handmatig beoordelen",
|
||||||
|
|||||||
@ -862,7 +862,6 @@
|
|||||||
"status.block": "Blokker @{name}",
|
"status.block": "Blokker @{name}",
|
||||||
"status.bookmark": "Set bokmerke",
|
"status.bookmark": "Set bokmerke",
|
||||||
"status.cancel_reblog_private": "Opphev framheving",
|
"status.cancel_reblog_private": "Opphev framheving",
|
||||||
"status.cannot_quote": "Du kan ikkje sitera dette innlegget",
|
|
||||||
"status.cannot_reblog": "Du kan ikkje framheva dette innlegget",
|
"status.cannot_reblog": "Du kan ikkje framheva dette innlegget",
|
||||||
"status.context.load_new_replies": "Nye svar finst",
|
"status.context.load_new_replies": "Nye svar finst",
|
||||||
"status.context.loading": "Ser etter fleire svar",
|
"status.context.loading": "Ser etter fleire svar",
|
||||||
|
|||||||
@ -848,7 +848,6 @@
|
|||||||
"status.block": "Zablokuj @{name}",
|
"status.block": "Zablokuj @{name}",
|
||||||
"status.bookmark": "Dodaj zakładkę",
|
"status.bookmark": "Dodaj zakładkę",
|
||||||
"status.cancel_reblog_private": "Cofnij podbicie",
|
"status.cancel_reblog_private": "Cofnij podbicie",
|
||||||
"status.cannot_quote": "Cytaty są wyłączone dla tego postu",
|
|
||||||
"status.cannot_reblog": "Ten wpis nie może zostać podbity",
|
"status.cannot_reblog": "Ten wpis nie może zostać podbity",
|
||||||
"status.context.load_new_replies": "Dostępne są nowe odpowiedzi",
|
"status.context.load_new_replies": "Dostępne są nowe odpowiedzi",
|
||||||
"status.context.loading": "Sprawdzanie kolejnych odpowiedzi",
|
"status.context.loading": "Sprawdzanie kolejnych odpowiedzi",
|
||||||
|
|||||||
@ -860,7 +860,6 @@
|
|||||||
"status.block": "Bloquear @{name}",
|
"status.block": "Bloquear @{name}",
|
||||||
"status.bookmark": "Guardar nos marcadores",
|
"status.bookmark": "Guardar nos marcadores",
|
||||||
"status.cancel_reblog_private": "Retirar impulso",
|
"status.cancel_reblog_private": "Retirar impulso",
|
||||||
"status.cannot_quote": "Citações estão desativadas nesta publicação",
|
|
||||||
"status.cannot_reblog": "Esta publicação não pode ser impulsionada",
|
"status.cannot_reblog": "Esta publicação não pode ser impulsionada",
|
||||||
"status.context.load_new_replies": "Novas respostas disponíveis",
|
"status.context.load_new_replies": "Novas respostas disponíveis",
|
||||||
"status.context.loading": "A verificar por mais respostas",
|
"status.context.loading": "A verificar por mais respostas",
|
||||||
|
|||||||
@ -862,7 +862,6 @@
|
|||||||
"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.load_new_replies": "Доступны новые ответы",
|
"status.context.load_new_replies": "Доступны новые ответы",
|
||||||
"status.context.loading": "Проверяем, есть ли ещё ответы",
|
"status.context.loading": "Проверяем, есть ли ещё ответы",
|
||||||
|
|||||||
@ -241,6 +241,8 @@
|
|||||||
"confirmations.mute.confirm": "Sessize al",
|
"confirmations.mute.confirm": "Sessize al",
|
||||||
"confirmations.quiet_post_quote_info.dismiss": "Bana bir daha hatırlatma",
|
"confirmations.quiet_post_quote_info.dismiss": "Bana bir daha hatırlatma",
|
||||||
"confirmations.quiet_post_quote_info.got_it": "Anladım",
|
"confirmations.quiet_post_quote_info.got_it": "Anladım",
|
||||||
|
"confirmations.quiet_post_quote_info.message": "Sessiz bir genel gönderiyi alıntıladığınızda, gönderiniz öne çıkan zaman çizelgelerinden gizlenir.",
|
||||||
|
"confirmations.quiet_post_quote_info.title": "Herkese açık sessiz gönderileri alıntılamak",
|
||||||
"confirmations.redraft.confirm": "Sil Düzenle ve yeniden paylaş",
|
"confirmations.redraft.confirm": "Sil Düzenle ve yeniden paylaş",
|
||||||
"confirmations.redraft.message": "Bu gönderiyi silip taslak haline getirmek istediğinize emin misiniz? Mevcut favoriler ve boostlar silinecek ve gönderiye verilen yanıtlar başıboş kalacak.",
|
"confirmations.redraft.message": "Bu gönderiyi silip taslak haline getirmek istediğinize emin misiniz? Mevcut favoriler ve boostlar silinecek ve gönderiye verilen yanıtlar başıboş kalacak.",
|
||||||
"confirmations.redraft.title": "Gönderiyi sil veya taslağa dönüştür?",
|
"confirmations.redraft.title": "Gönderiyi sil veya taslağa dönüştür?",
|
||||||
@ -451,10 +453,12 @@
|
|||||||
"ignore_notifications_modal.private_mentions_title": "İstenmeyen özel bahsetmelerden gelen bildirimleri yoksay?",
|
"ignore_notifications_modal.private_mentions_title": "İstenmeyen özel bahsetmelerden gelen bildirimleri yoksay?",
|
||||||
"info_button.label": "Yardım",
|
"info_button.label": "Yardım",
|
||||||
"info_button.what_is_alt_text": "<h1>Alternatif metin nedir?</h1><p>Alternatif metin, görme bozukluğu olan, düşük bant genişliğine sahip bağlantıları olan veya ekstra bağlam arayan kişiler için görsel açıklamaları sağlar.</p><p>Net, sade ve nesnel alternatif metin yazarak herkes için erişilebilirliği ve anlaşılabilirliği iyileştirebilirsiniz.</p><ul><li>Önemlileri yakalayın</li><li>Resimlerdeki metni özetleyin</li><li>Düzenli cümle yapısı kullanın</li><li>Gereksiz bilgilerden kaçının</li><li>Karmaşık görsellerde (şemalar veya haritalar gibi) trendlere ve temel bulgulara odaklanın</li></ul>",
|
"info_button.what_is_alt_text": "<h1>Alternatif metin nedir?</h1><p>Alternatif metin, görme bozukluğu olan, düşük bant genişliğine sahip bağlantıları olan veya ekstra bağlam arayan kişiler için görsel açıklamaları sağlar.</p><p>Net, sade ve nesnel alternatif metin yazarak herkes için erişilebilirliği ve anlaşılabilirliği iyileştirebilirsiniz.</p><ul><li>Önemlileri yakalayın</li><li>Resimlerdeki metni özetleyin</li><li>Düzenli cümle yapısı kullanın</li><li>Gereksiz bilgilerden kaçının</li><li>Karmaşık görsellerde (şemalar veya haritalar gibi) trendlere ve temel bulgulara odaklanın</li></ul>",
|
||||||
|
"interaction_modal.action": "{name}'nin gönderisiyle etkileşim kurmak için, kullandığınız Mastodon sunucusunda hesabınıza giriş yapmanız gerekir.",
|
||||||
"interaction_modal.go": "Git",
|
"interaction_modal.go": "Git",
|
||||||
"interaction_modal.no_account_yet": "Henüz bir hesabınız yok mu?",
|
"interaction_modal.no_account_yet": "Henüz bir hesabınız yok mu?",
|
||||||
"interaction_modal.on_another_server": "Farklı bir sunucuda",
|
"interaction_modal.on_another_server": "Farklı bir sunucuda",
|
||||||
"interaction_modal.on_this_server": "Bu sunucuda",
|
"interaction_modal.on_this_server": "Bu sunucuda",
|
||||||
|
"interaction_modal.title": "Devam etmek için oturum açın",
|
||||||
"interaction_modal.username_prompt": "Örnek: {example}",
|
"interaction_modal.username_prompt": "Örnek: {example}",
|
||||||
"intervals.full.days": "{number, plural, one {# gün} other {# gün}}",
|
"intervals.full.days": "{number, plural, one {# gün} other {# gün}}",
|
||||||
"intervals.full.hours": "{number, plural, one {# saat} other {# saat}}",
|
"intervals.full.hours": "{number, plural, one {# saat} other {# saat}}",
|
||||||
@ -737,6 +741,7 @@
|
|||||||
"privacy.quote.disabled": "{visibility}, alıntı yapılamaz",
|
"privacy.quote.disabled": "{visibility}, alıntı yapılamaz",
|
||||||
"privacy.quote.limited": "{visibility}, sınırlı alıntı",
|
"privacy.quote.limited": "{visibility}, sınırlı alıntı",
|
||||||
"privacy.unlisted.additional": "Bu neredeyse herkese açık gibi çalışır, tek farkı gönderi canlı akışlarda veya etiketlerde, keşfette, veya Mastodon aramasında gözükmez, hesap çapında öyle seçmiş olsanız bile.",
|
"privacy.unlisted.additional": "Bu neredeyse herkese açık gibi çalışır, tek farkı gönderi canlı akışlarda veya etiketlerde, keşfette, veya Mastodon aramasında gözükmez, hesap çapında öyle seçmiş olsanız bile.",
|
||||||
|
"privacy.unlisted.long": "Mastodon arama sonuçlarında, öne çıkanlarda ve herkese açık zaman çizelgesinde gizli",
|
||||||
"privacy.unlisted.short": "Sessizce herkese açık",
|
"privacy.unlisted.short": "Sessizce herkese açık",
|
||||||
"privacy_policy.last_updated": "Son güncelleme {date}",
|
"privacy_policy.last_updated": "Son güncelleme {date}",
|
||||||
"privacy_policy.title": "Gizlilik Politikası",
|
"privacy_policy.title": "Gizlilik Politikası",
|
||||||
@ -759,6 +764,9 @@
|
|||||||
"relative_time.minutes": "{number}m",
|
"relative_time.minutes": "{number}m",
|
||||||
"relative_time.seconds": "{number}sn",
|
"relative_time.seconds": "{number}sn",
|
||||||
"relative_time.today": "bugün",
|
"relative_time.today": "bugün",
|
||||||
|
"remove_quote_hint.button_label": "Anladım",
|
||||||
|
"remove_quote_hint.message": "Bunu {icon} seçenekler menüsünden yapabilirsiniz.",
|
||||||
|
"remove_quote_hint.title": "Alıntılanmış gönderinizi kaldırmak mı istiyorsunuz?",
|
||||||
"reply_indicator.attachments": "{count, plural, one {# ek} other {# ek}}",
|
"reply_indicator.attachments": "{count, plural, one {# ek} other {# ek}}",
|
||||||
"reply_indicator.cancel": "İptal",
|
"reply_indicator.cancel": "İptal",
|
||||||
"reply_indicator.poll": "Anket",
|
"reply_indicator.poll": "Anket",
|
||||||
@ -854,6 +862,7 @@
|
|||||||
"status.block": "@{name} adlı kişiyi engelle",
|
"status.block": "@{name} adlı kişiyi engelle",
|
||||||
"status.bookmark": "Yer işareti ekle",
|
"status.bookmark": "Yer işareti ekle",
|
||||||
"status.cancel_reblog_private": "Yeniden paylaşımı geri al",
|
"status.cancel_reblog_private": "Yeniden paylaşımı geri al",
|
||||||
|
"status.cannot_quote": "Bu gönderiyi alıntılamaya izniniz yok",
|
||||||
"status.cannot_reblog": "Bu gönderi yeniden paylaşılamaz",
|
"status.cannot_reblog": "Bu gönderi yeniden paylaşılamaz",
|
||||||
"status.context.load_new_replies": "Yeni yanıtlar mevcut",
|
"status.context.load_new_replies": "Yeni yanıtlar mevcut",
|
||||||
"status.context.loading": "Daha fazla yanıt için kontrol ediliyor",
|
"status.context.loading": "Daha fazla yanıt için kontrol ediliyor",
|
||||||
@ -888,6 +897,8 @@
|
|||||||
"status.quote_error.filtered": "Bazı filtrelerinizden dolayı gizlenmiştir",
|
"status.quote_error.filtered": "Bazı filtrelerinizden dolayı gizlenmiştir",
|
||||||
"status.quote_error.not_available": "Gönderi kullanılamıyor",
|
"status.quote_error.not_available": "Gönderi kullanılamıyor",
|
||||||
"status.quote_error.pending_approval": "Gönderi beklemede",
|
"status.quote_error.pending_approval": "Gönderi beklemede",
|
||||||
|
"status.quote_error.pending_approval_popout.body": "Mastodon'da, birinin sizi alıntılayıp alıntılayamayacağını kontrol edebilirsiniz. Bu gönderi, orijinal yazarın onayını alma sürecinde beklemede.",
|
||||||
|
"status.quote_error.revoked": "Gönderi yazarı tarafından kaldırıldı",
|
||||||
"status.quote_followers_only": "Sadece takipçiler bu gönderiyi alıntılayabilir",
|
"status.quote_followers_only": "Sadece takipçiler bu gönderiyi alıntılayabilir",
|
||||||
"status.quote_manual_review": "Yazar manuel olarak gözden geçirecek",
|
"status.quote_manual_review": "Yazar manuel olarak gözden geçirecek",
|
||||||
"status.quote_policy_change": "Kimin alıntı yapabileceğini değiştirin",
|
"status.quote_policy_change": "Kimin alıntı yapabileceğini değiştirin",
|
||||||
@ -898,12 +909,14 @@
|
|||||||
"status.read_more": "Devamını okuyun",
|
"status.read_more": "Devamını okuyun",
|
||||||
"status.reblog": "Yeniden paylaş",
|
"status.reblog": "Yeniden paylaş",
|
||||||
"status.reblog_or_quote": "Yükselt veya alıntıla",
|
"status.reblog_or_quote": "Yükselt veya alıntıla",
|
||||||
|
"status.reblog_private": "Takipçilerinizle tekrar paylaşın",
|
||||||
"status.reblogged_by": "{name} yeniden paylaştı",
|
"status.reblogged_by": "{name} yeniden paylaştı",
|
||||||
"status.reblogs": "{count, plural, one {yeniden paylaşım} other {yeniden paylaşım}}",
|
"status.reblogs": "{count, plural, one {yeniden paylaşım} other {yeniden paylaşım}}",
|
||||||
"status.reblogs.empty": "Henüz hiç kimse bu gönderiyi yeniden paylaşmadı. Herhangi bir kullanıcı yeniden paylaştığında burada görüntülenecek.",
|
"status.reblogs.empty": "Henüz hiç kimse bu gönderiyi yeniden paylaşmadı. Herhangi bir kullanıcı yeniden paylaştığında burada görüntülenecek.",
|
||||||
"status.redraft": "Sil,Düzenle ve yeniden-paylaş",
|
"status.redraft": "Sil,Düzenle ve yeniden-paylaş",
|
||||||
"status.remove_bookmark": "Yer işaretini kaldır",
|
"status.remove_bookmark": "Yer işaretini kaldır",
|
||||||
"status.remove_favourite": "Favorilerden kaldır",
|
"status.remove_favourite": "Favorilerden kaldır",
|
||||||
|
"status.remove_quote": "Kaldır",
|
||||||
"status.replied_in_thread": "Akışta yanıtlandı",
|
"status.replied_in_thread": "Akışta yanıtlandı",
|
||||||
"status.replied_to": "{name} kullanıcısına yanıt verdi",
|
"status.replied_to": "{name} kullanıcısına yanıt verdi",
|
||||||
"status.reply": "Yanıtla",
|
"status.reply": "Yanıtla",
|
||||||
@ -975,9 +988,14 @@
|
|||||||
"visibility_modal.button_title": "Görünürlüğü ayarla",
|
"visibility_modal.button_title": "Görünürlüğü ayarla",
|
||||||
"visibility_modal.header": "Görünürlük ve etkileşim",
|
"visibility_modal.header": "Görünürlük ve etkileşim",
|
||||||
"visibility_modal.helper.direct_quoting": "Mastodon'da özel değiniler başkaları tarafından alıntılanamaz.",
|
"visibility_modal.helper.direct_quoting": "Mastodon'da özel değiniler başkaları tarafından alıntılanamaz.",
|
||||||
|
"visibility_modal.helper.privacy_editing": "Gönderi yayınlandıktan sonra görünürlük değiştirilemez.",
|
||||||
|
"visibility_modal.helper.privacy_private_self_quote": "Özel gönderilerin kendi alıntıları herkese açık hale getirilemez.",
|
||||||
"visibility_modal.helper.private_quoting": "Mastodon'da sadece takipçilere yönelik gönderiler başkaları tarafından alıntılanamaz.",
|
"visibility_modal.helper.private_quoting": "Mastodon'da sadece takipçilere yönelik gönderiler başkaları tarafından alıntılanamaz.",
|
||||||
"visibility_modal.helper.unlisted_quoting": "İnsanlar sizden alıntı yaptığında, onların gönderileri de trend zaman tünellerinden gizlenecektir.",
|
"visibility_modal.helper.unlisted_quoting": "İnsanlar sizden alıntı yaptığında, onların gönderileri de trend zaman tünellerinden gizlenecektir.",
|
||||||
|
"visibility_modal.instructions": "Bu gönderiyle kimlerin etkileşime girebileceğini kontrol edin. Ayrıca, <link>Tercihler > Gönderme varsayılanları</link> bölümüne giderek tüm gelecek gönderiler için ayarlayabilirsiniz.",
|
||||||
|
"visibility_modal.privacy_label": "Görünürlük",
|
||||||
"visibility_modal.quote_followers": "Sadece takipçiler",
|
"visibility_modal.quote_followers": "Sadece takipçiler",
|
||||||
|
"visibility_modal.quote_label": "Kimler alıntılayabilir",
|
||||||
"visibility_modal.quote_nobody": "Sadece ben",
|
"visibility_modal.quote_nobody": "Sadece ben",
|
||||||
"visibility_modal.quote_public": "Herkesten",
|
"visibility_modal.quote_public": "Herkesten",
|
||||||
"visibility_modal.save": "Kaydet"
|
"visibility_modal.save": "Kaydet"
|
||||||
|
|||||||
@ -860,7 +860,6 @@
|
|||||||
"status.block": "Chặn @{name}",
|
"status.block": "Chặn @{name}",
|
||||||
"status.bookmark": "Lưu",
|
"status.bookmark": "Lưu",
|
||||||
"status.cancel_reblog_private": "Bỏ đăng lại",
|
"status.cancel_reblog_private": "Bỏ đăng lại",
|
||||||
"status.cannot_quote": "Trích dẫn bị tắt",
|
|
||||||
"status.cannot_reblog": "Không thể đăng lại tút này",
|
"status.cannot_reblog": "Không thể đăng lại tút này",
|
||||||
"status.context.load_new_replies": "Có những trả lời mới",
|
"status.context.load_new_replies": "Có những trả lời mới",
|
||||||
"status.context.loading": "Kiểm tra nhiều trả lời hơn",
|
"status.context.loading": "Kiểm tra nhiều trả lời hơn",
|
||||||
|
|||||||
@ -453,10 +453,12 @@
|
|||||||
"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>什么是替代文本?</h1> <p>替代文本为视力障碍者、低带宽连接用户或需要额外背景信息的用户提供图像描述。</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.go": "跳转",
|
"interaction_modal.go": "跳转",
|
||||||
"interaction_modal.no_account_yet": "还没有账号?",
|
"interaction_modal.no_account_yet": "还没有账号?",
|
||||||
"interaction_modal.on_another_server": "在另一服务器",
|
"interaction_modal.on_another_server": "在另一服务器",
|
||||||
"interaction_modal.on_this_server": "在此服务器",
|
"interaction_modal.on_this_server": "在此服务器",
|
||||||
|
"interaction_modal.title": "登录以继续",
|
||||||
"interaction_modal.username_prompt": "例如: {example}",
|
"interaction_modal.username_prompt": "例如: {example}",
|
||||||
"intervals.full.days": "{number} 天",
|
"intervals.full.days": "{number} 天",
|
||||||
"intervals.full.hours": "{number} 小时",
|
"intervals.full.hours": "{number} 小时",
|
||||||
@ -860,7 +862,7 @@
|
|||||||
"status.block": "屏蔽 @{name}",
|
"status.block": "屏蔽 @{name}",
|
||||||
"status.bookmark": "添加到书签",
|
"status.bookmark": "添加到书签",
|
||||||
"status.cancel_reblog_private": "取消转嘟",
|
"status.cancel_reblog_private": "取消转嘟",
|
||||||
"status.cannot_quote": "此嘟文已禁用引用功能",
|
"status.cannot_quote": "你无法引用此嘟文",
|
||||||
"status.cannot_reblog": "不能转嘟这条嘟文",
|
"status.cannot_reblog": "不能转嘟这条嘟文",
|
||||||
"status.context.load_new_replies": "有新回复",
|
"status.context.load_new_replies": "有新回复",
|
||||||
"status.context.loading": "正在检查更多回复",
|
"status.context.loading": "正在检查更多回复",
|
||||||
|
|||||||
@ -862,7 +862,7 @@
|
|||||||
"status.block": "封鎖 @{name}",
|
"status.block": "封鎖 @{name}",
|
||||||
"status.bookmark": "書籤",
|
"status.bookmark": "書籤",
|
||||||
"status.cancel_reblog_private": "取消轉嘟",
|
"status.cancel_reblog_private": "取消轉嘟",
|
||||||
"status.cannot_quote": "此嘟文已停用引用功能",
|
"status.cannot_quote": "您不被允許引用此嘟文",
|
||||||
"status.cannot_reblog": "這則嘟文無法被轉嘟",
|
"status.cannot_reblog": "這則嘟文無法被轉嘟",
|
||||||
"status.context.load_new_replies": "有新回嘟",
|
"status.context.load_new_replies": "有新回嘟",
|
||||||
"status.context.loading": "正在檢查更多回嘟",
|
"status.context.loading": "正在檢查更多回嘟",
|
||||||
|
|||||||
@ -10294,7 +10294,7 @@ noscript {
|
|||||||
.notification-list {
|
.notification-list {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 2rem;
|
bottom: 2rem;
|
||||||
inset-inline-start: 0;
|
inset-inline-start: 1rem;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -10302,9 +10302,11 @@ noscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.notification-bar {
|
.notification-bar {
|
||||||
|
--alert-edge-spacing: 1rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
position: relative;
|
|
||||||
inset-inline-start: -100%;
|
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -10320,33 +10322,43 @@ noscript {
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 21px;
|
line-height: 21px;
|
||||||
|
|
||||||
&.notification-bar-active {
|
&.from-side {
|
||||||
inset-inline-start: 1rem;
|
translate: calc(
|
||||||
|
-1 * (100% + var(--alert-edge-spacing)) * var(--text-x-direction)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.from-below {
|
||||||
|
translate: 0 calc(100% + var(--alert-edge-spacing));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.notification-bar--active {
|
||||||
|
translate: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-reduce-motion & {
|
.no-reduce-motion & {
|
||||||
transition: 0.5s cubic-bezier(0.89, 0.01, 0.5, 1.1);
|
transition: 0.5s cubic-bezier(0.89, 0.01, 0.5, 1.1);
|
||||||
transform: translateZ(0);
|
will-change: translate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-bar-title {
|
.notification-bar__content {
|
||||||
margin-inline-end: 5px;
|
margin-inline-end: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-bar-title,
|
.notification-bar__title {
|
||||||
.notification-bar-action {
|
margin-inline-end: 5px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-bar-action {
|
.notification-bar__action {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 0;
|
border: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
margin-inline-start: 10px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: $blurple-300;
|
color: $blurple-300;
|
||||||
|
font-weight: 700;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
|
|
||||||
@ -10357,6 +10369,17 @@ noscript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notification-bar__dismiss-button {
|
||||||
|
margin-top: -2px;
|
||||||
|
color: rgb(from currentColor r g b / 85%);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
color: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.hashtag-header {
|
.hashtag-header {
|
||||||
border-bottom: 1px solid var(--background-border-color);
|
border-bottom: 1px solid var(--background-border-color);
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
|||||||
@ -65,7 +65,7 @@ class PostStatusService < BaseService
|
|||||||
private
|
private
|
||||||
|
|
||||||
def fill_blank_text!
|
def fill_blank_text!
|
||||||
return unless @text.blank? && @options[:spoiler_text].present?
|
return unless @text.blank? && @options[:spoiler_text].present? && @quoted_status.blank?
|
||||||
|
|
||||||
@text = begin
|
@text = begin
|
||||||
if @media&.any?(&:video?) || @media&.any?(&:gifv?)
|
if @media&.any?(&:video?) || @media&.any?(&:gifv?)
|
||||||
|
|||||||
@ -56,6 +56,8 @@ tr:
|
|||||||
scopes: Uygulamanın erişmesine izin verilen API'ler. Üst seviye bir kapsam seçtiyseniz, bireysel kapsam seçmenize gerek yoktur.
|
scopes: Uygulamanın erişmesine izin verilen API'ler. Üst seviye bir kapsam seçtiyseniz, bireysel kapsam seçmenize gerek yoktur.
|
||||||
setting_aggregate_reblogs: Yakın zamanda teşvik edilmiş gönderiler için yeni teşvikleri göstermeyin (yalnızca yeni alınan teşvikleri etkiler)
|
setting_aggregate_reblogs: Yakın zamanda teşvik edilmiş gönderiler için yeni teşvikleri göstermeyin (yalnızca yeni alınan teşvikleri etkiler)
|
||||||
setting_always_send_emails: Normalde, Mastodon'u aktif olarak kullanırken e-posta bildirimleri gönderilmeyecektir
|
setting_always_send_emails: Normalde, Mastodon'u aktif olarak kullanırken e-posta bildirimleri gönderilmeyecektir
|
||||||
|
setting_default_quote_policy_private: Mastodon'da sadece takipçilere yönelik gönderiler başkaları tarafından alıntılanamaz.
|
||||||
|
setting_default_quote_policy_unlisted: İnsanlar sizden alıntı yaptığında, onların gönderileri de trend zaman tünellerinden gizlenecektir.
|
||||||
setting_default_sensitive: Hassas medya varsayılan olarak gizlidir ve bir tıklama ile gösterilebilir
|
setting_default_sensitive: Hassas medya varsayılan olarak gizlidir ve bir tıklama ile gösterilebilir
|
||||||
setting_display_media_default: Hassas olarak işaretlenmiş medyayı gizle
|
setting_display_media_default: Hassas olarak işaretlenmiş medyayı gizle
|
||||||
setting_display_media_hide_all: Medyayı her zaman gizle
|
setting_display_media_hide_all: Medyayı her zaman gizle
|
||||||
|
|||||||
@ -1739,6 +1739,9 @@ tr:
|
|||||||
self_vote: Kendi anketlerinizde oy kullanamazsınız
|
self_vote: Kendi anketlerinizde oy kullanamazsınız
|
||||||
too_few_options: birden fazla öğeye sahip olmalı
|
too_few_options: birden fazla öğeye sahip olmalı
|
||||||
too_many_options: "%{max} öğeden fazla öğe içeremez"
|
too_many_options: "%{max} öğeden fazla öğe içeremez"
|
||||||
|
vote: Oyla
|
||||||
|
posting_defaults:
|
||||||
|
explanation: Bu ayarlar, yeni gönderiler oluştururken varsayılan olarak kullanılacaktır, ancak bunları oluşturucu içinde her gönderi için ayrı ayrı düzenleyebilirsiniz.
|
||||||
preferences:
|
preferences:
|
||||||
other: Diğer
|
other: Diğer
|
||||||
posting_defaults: Gönderi varsayılanları
|
posting_defaults: Gönderi varsayılanları
|
||||||
@ -1915,8 +1918,10 @@ tr:
|
|||||||
title: '%{name}: "%{quote}"'
|
title: '%{name}: "%{quote}"'
|
||||||
visibilities:
|
visibilities:
|
||||||
direct: Özel değini
|
direct: Özel değini
|
||||||
|
private: Sadece takipçiler
|
||||||
public: Herkese açık
|
public: Herkese açık
|
||||||
public_long: Mastodon'da olan olmayan herkes
|
public_long: Mastodon'da olan olmayan herkes
|
||||||
|
unlisted: Sessizce herkese açık
|
||||||
unlisted_long: Mastodon arama sonuçlarında, öne çıkanlarda ve herkese açık zaman çizelgesinde gizli
|
unlisted_long: Mastodon arama sonuçlarında, öne çıkanlarda ve herkese açık zaman çizelgesinde gizli
|
||||||
statuses_cleanup:
|
statuses_cleanup:
|
||||||
enabled: Eski gönderileri otomatik olarak sil
|
enabled: Eski gönderileri otomatik olarak sil
|
||||||
|
|||||||
@ -59,7 +59,7 @@ services:
|
|||||||
web:
|
web:
|
||||||
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
||||||
# build: .
|
# build: .
|
||||||
image: ghcr.io/glitch-soc/mastodon:v4.4.3
|
image: ghcr.io/glitch-soc/mastodon:v4.4.4
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: bundle exec puma -C config/puma.rb
|
command: bundle exec puma -C config/puma.rb
|
||||||
@ -83,7 +83,7 @@ services:
|
|||||||
# build:
|
# build:
|
||||||
# dockerfile: ./streaming/Dockerfile
|
# dockerfile: ./streaming/Dockerfile
|
||||||
# context: .
|
# context: .
|
||||||
image: ghcr.io/glitch-soc/mastodon-streaming:v4.4.3
|
image: ghcr.io/glitch-soc/mastodon-streaming:v4.4.4
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: node ./streaming/index.js
|
command: node ./streaming/index.js
|
||||||
@ -102,7 +102,7 @@ services:
|
|||||||
sidekiq:
|
sidekiq:
|
||||||
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
||||||
# build: .
|
# build: .
|
||||||
image: ghcr.io/glitch-soc/mastodon:v4.4.3
|
image: ghcr.io/glitch-soc/mastodon:v4.4.4
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: bundle exec sidekiq
|
command: bundle exec sidekiq
|
||||||
|
|||||||
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