kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Merge branch 'next_' into 'next'
next See merge request soapbox-pub/soapbox-fe!1176next-virtuoso-proof
commit
9d996b355d
|
@ -236,7 +236,10 @@ export function submitCompose(routerHistory, force = false) {
|
|||
|
||||
if (!force && needsDescriptions(state)) {
|
||||
dispatch(openModal('MISSING_DESCRIPTION', {
|
||||
onContinue: () => dispatch(submitCompose(routerHistory, true)),
|
||||
onContinue: () => {
|
||||
dispatch(closeModal('MISSING_DESCRIPTION'));
|
||||
dispatch(submitCompose(routerHistory, true));
|
||||
},
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import Icon from 'soapbox/components/icon';
|
||||
|
||||
const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
|
||||
|
||||
export default class AttachmentList extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
media: ImmutablePropTypes.list.isRequired,
|
||||
compact: PropTypes.bool,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { media, compact } = this.props;
|
||||
|
||||
if (compact) {
|
||||
return (
|
||||
<div className='attachment-list compact'>
|
||||
<ul className='attachment-list__list'>
|
||||
{media.map(attachment => {
|
||||
const displayUrl = attachment.get('remote_url') || attachment.get('url');
|
||||
|
||||
return (
|
||||
<li key={attachment.get('id')}>
|
||||
<a href={displayUrl} target='_blank' rel='noopener'><Icon id='link' /> {filename(displayUrl)}</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='attachment-list'>
|
||||
<div className='attachment-list__icon'>
|
||||
<Icon id='link' />
|
||||
</div>
|
||||
|
||||
<ul className='attachment-list__list'>
|
||||
{media.map(attachment => {
|
||||
const displayUrl = attachment.get('remote_url') || attachment.get('url');
|
||||
|
||||
return (
|
||||
<li key={attachment.get('id')}>
|
||||
<a href={displayUrl} target='_blank' rel='noopener'>{filename(displayUrl)}</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import React from 'react';
|
||||
|
||||
import Icon from 'soapbox/components/icon';
|
||||
|
||||
import type { Attachment as AttachmentEntity } from 'soapbox/types/entities';
|
||||
|
||||
const filename = (url: string) => url.split('/').pop()!.split('#')[0].split('?')[0];
|
||||
|
||||
interface IAttachmentList {
|
||||
media: AttachmentEntity[],
|
||||
compact?: boolean,
|
||||
}
|
||||
|
||||
const AttachmentList: React.FC<IAttachmentList> = ({ media, compact }) => {
|
||||
if (compact) {
|
||||
return (
|
||||
<div className='attachment-list compact'>
|
||||
<ul className='attachment-list__list'>
|
||||
{media.map(attachment => {
|
||||
const displayUrl = attachment.get('remote_url') || attachment.get('url');
|
||||
|
||||
return (
|
||||
<li key={attachment.get('id')}>
|
||||
<a href={displayUrl} target='_blank' rel='noopener'><Icon id='link' /> {filename(displayUrl)}</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='attachment-list'>
|
||||
<div className='attachment-list__icon'>
|
||||
<Icon id='link' />
|
||||
</div>
|
||||
|
||||
<ul className='attachment-list__list'>
|
||||
{media.map(attachment => {
|
||||
const displayUrl = attachment.get('remote_url') || attachment.get('url');
|
||||
|
||||
return (
|
||||
<li key={attachment.get('id')}>
|
||||
<a href={displayUrl} target='_blank' rel='noopener'>{filename(displayUrl)}</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AttachmentList;
|
|
@ -11,6 +11,7 @@ import { Link } from 'react-router-dom';
|
|||
import { fetchBirthdayReminders } from 'soapbox/actions/accounts';
|
||||
import { openModal } from 'soapbox/actions/modals';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import { HStack, Text } from 'soapbox/components/ui';
|
||||
import { makeGetAccount } from 'soapbox/selectors';
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
|
@ -72,7 +73,7 @@ class BirthdayReminders extends ImmutablePureComponent {
|
|||
const link = (
|
||||
<bdi>
|
||||
<Link
|
||||
className='notification__display-name'
|
||||
className='text-gray-800 dark:text-gray-200 font-bold hover:underline'
|
||||
title={account.get('acct')}
|
||||
to={`/@${account.get('acct')}`}
|
||||
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
|
||||
|
@ -137,14 +138,20 @@ class BirthdayReminders extends ImmutablePureComponent {
|
|||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification-birthday focusable' tabIndex='0' title={this.renderMessageForScreenReader()}>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__icon-wrapper'>
|
||||
<Icon src={require('@tabler/icons/icons/ballon.svg')} />
|
||||
</div>
|
||||
<div className='p-4 focusable'>
|
||||
<HStack alignItems='center' space={1.5}>
|
||||
<Icon
|
||||
src={require('@tabler/icons/icons/ballon.svg')}
|
||||
className='text-primary-600'
|
||||
/>
|
||||
|
||||
<span>
|
||||
{this.renderMessage()}
|
||||
</span>
|
||||
<Text
|
||||
theme='muted'
|
||||
size='sm'
|
||||
>
|
||||
{this.renderMessage()}
|
||||
</Text>
|
||||
</HStack>
|
||||
</div>
|
||||
</div>
|
||||
</HotKeys>
|
||||
|
|
|
@ -379,7 +379,7 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
prepend = (
|
||||
<div className='pt-4 px-4'>
|
||||
<HStack alignItems='center' space={1}>
|
||||
<Icon src={require('@tabler/icons/icons/pinned.svg')} className='text-gray-600' />
|
||||
<Icon src={require('@tabler/icons/icons/pinned.svg')} className='text-gray-600 dark:text-gray-400' />
|
||||
|
||||
<Text size='sm' theme='muted' weight='medium'>
|
||||
<FormattedMessage id='status.pinned' defaultMessage='Pinned post' />
|
||||
|
|
|
@ -28,7 +28,7 @@ const Card: React.FC<ICard> = React.forwardRef(({ children, variant, size = 'md'
|
|||
{...filteredProps}
|
||||
className={classNames({
|
||||
'space-y-4': true,
|
||||
'bg-white dark:bg-slate-800 sm:shadow-lg dark:sm:shadow-inset overflow-hidden': variant === 'rounded',
|
||||
'bg-white dark:bg-slate-800 text-black dark:text-white sm:shadow-lg dark:sm:shadow-inset overflow-hidden': variant === 'rounded',
|
||||
[sizes[size]]: true,
|
||||
}, className)}
|
||||
>
|
||||
|
|
|
@ -34,7 +34,7 @@ const Column: React.FC<IColumn> = React.forwardRef((props, ref: React.ForwardedR
|
|||
|
||||
const renderChildren = () => {
|
||||
if (transparent) {
|
||||
return <div className={classNames('bg-white dark:bg-slate-800 sm:bg-transparent sm:dark:bg-transparent', className)}>{children}</div>;
|
||||
return <div className={classNames('bg-white dark:bg-slate-800 text-black dark:text-white sm:bg-transparent sm:dark:bg-transparent', className)}>{children}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -46,7 +46,7 @@ const Modal: React.FC<IModal> = ({
|
|||
}, [buttonRef]);
|
||||
|
||||
return (
|
||||
<div data-testid='modal' className='block w-full max-w-xl p-6 mx-auto overflow-hidden text-left align-middle transition-all transform bg-white dark:bg-slate-800 shadow-xl rounded-2xl pointer-events-auto'>
|
||||
<div data-testid='modal' className='block w-full max-w-xl p-6 mx-auto overflow-hidden text-left align-middle transition-all transform bg-white dark:bg-slate-800 text-black dark:text-white shadow-xl rounded-2xl pointer-events-auto'>
|
||||
<div className='sm:flex sm:items-start w-full justify-between'>
|
||||
<div className='w-full'>
|
||||
<div className='w-full flex flex-row justify-between items-center'>
|
||||
|
|
|
@ -75,6 +75,7 @@ class DirectTimeline extends React.PureComponent {
|
|||
timelineId='direct'
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
|
||||
divideType='space'
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
|
|
|
@ -61,7 +61,7 @@ class ConfirmationModal extends React.PureComponent {
|
|||
cancelText={<FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />}
|
||||
cancelAction={this.handleCancel}
|
||||
secondaryText={secondary}
|
||||
sectondaryAction={this.handleSecondary}
|
||||
secondaryAction={this.props.onSecondary && this.handleSecondary}
|
||||
>
|
||||
<p className='text-gray-600 dark:text-gray-300'>{message}</p>
|
||||
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Button } from '../../../components/ui';
|
||||
|
||||
export default @injectIntl
|
||||
class MissingDescriptionModal extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
onClose: PropTypes.func,
|
||||
onContinue: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.button.focus();
|
||||
}
|
||||
|
||||
handleContinue = () => {
|
||||
this.props.onClose();
|
||||
this.props.onContinue();
|
||||
}
|
||||
|
||||
handleCancel = () => {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
setRef = (c) => {
|
||||
this.button = c;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='modal-root__modal confirmation-modal'>
|
||||
<div className='confirmation-modal__container'>
|
||||
<FormattedMessage id='missing_description_modal.text' defaultMessage='You have not entered a description for all attachments. Continue anyway?' />
|
||||
</div>
|
||||
|
||||
<div className='confirmation-modal__action-bar'>
|
||||
<Button onClick={this.handleCancel} className='confirmation-modal__cancel-button' ref={this.setRef}>
|
||||
<FormattedMessage id='missing_description_modal.cancel' defaultMessage='Cancel' />
|
||||
</Button>
|
||||
<Button onClick={this.handleContinue}>
|
||||
<FormattedMessage id='missing_description_modal.continue' defaultMessage='Post' />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import React from 'react';
|
||||
import { injectIntl, FormattedMessage, IntlShape, defineMessages } from 'react-intl';
|
||||
|
||||
import { Modal } from 'soapbox/components/ui';
|
||||
|
||||
const messages = defineMessages({
|
||||
modalTitle: { id: 'missing_description_modal.text', defaultMessage: 'You have not entered a description for all attachments.' },
|
||||
post: { id: 'missing_description_modal.continue', defaultMessage: 'Post' },
|
||||
cancel: { id: 'missing_description_modal.cancel', defaultMessage: 'Cancel' },
|
||||
});
|
||||
|
||||
interface IMissingDescriptionModal {
|
||||
onClose: () => void,
|
||||
onContinue: () => void,
|
||||
intl: IntlShape,
|
||||
}
|
||||
|
||||
const MissingDescriptionModal: React.FC<IMissingDescriptionModal> = ({ onClose, onContinue, intl }) => {
|
||||
return (
|
||||
<Modal
|
||||
title={intl.formatMessage(messages.modalTitle)}
|
||||
confirmationAction={onContinue}
|
||||
confirmationText={intl.formatMessage(messages.post)}
|
||||
confirmationTheme='danger'
|
||||
cancelText={intl.formatMessage(messages.cancel)}
|
||||
cancelAction={onClose}
|
||||
>
|
||||
<p className='text-gray-600 dark:text-gray-300'>
|
||||
<FormattedMessage id='missing_description_modal.description' defaultMessage='Continue anyway?' />
|
||||
</p>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default injectIntl(MissingDescriptionModal);
|
|
@ -92,7 +92,7 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
|||
<HStack alignItems='center' space={0.5}>
|
||||
<Icon
|
||||
src={require('@tabler/icons/icons/ballon.svg')}
|
||||
className='w-4 h-4 text-gray-800'
|
||||
className='w-4 h-4 text-gray-800 dark:text-gray-200'
|
||||
/>
|
||||
|
||||
<Text size='sm'>
|
||||
|
@ -191,7 +191,7 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
|||
<HStack alignItems='center' space={0.5}>
|
||||
<Icon
|
||||
src={require('@tabler/icons/icons/calendar.svg')}
|
||||
className='w-4 h-4 text-gray-800'
|
||||
className='w-4 h-4 text-gray-800 dark:text-gray-200'
|
||||
/>
|
||||
|
||||
<Text size='sm'>
|
||||
|
@ -208,7 +208,7 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
|||
<HStack alignItems='center' space={0.5}>
|
||||
<Icon
|
||||
src={require('@tabler/icons/icons/map-pin.svg')}
|
||||
className='w-4 h-4 text-gray-800'
|
||||
className='w-4 h-4 text-gray-800 dark:text-gray-200'
|
||||
/>
|
||||
|
||||
<Text size='sm'>
|
||||
|
@ -221,13 +221,13 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
|||
<HStack alignItems='center' space={0.5}>
|
||||
<Icon
|
||||
src={require('@tabler/icons/icons/link.svg')}
|
||||
className='w-4 h-4 text-gray-800'
|
||||
className='w-4 h-4 text-gray-800 dark:text-gray-200'
|
||||
/>
|
||||
|
||||
<div className='max-w-[300px]'>
|
||||
<Text size='sm' truncate>
|
||||
{isSafeUrl(account.get('website')) ? (
|
||||
<a className='text-primary-600 hover:underline' href={account.get('website')} target='_blank'>{account.get('website')}</a>
|
||||
<a className='text-primary-600 dark:text-primary-400 hover:underline' href={account.get('website')} target='_blank'>{account.get('website')}</a>
|
||||
) : (
|
||||
account.get('website')
|
||||
)}
|
||||
|
|
|
@ -107,6 +107,7 @@ class ReactionsModal extends React.PureComponent {
|
|||
<ScrollableList
|
||||
scrollKey='reactions'
|
||||
emptyMessage={emptyMessage}
|
||||
className='space-y-3'
|
||||
>
|
||||
{accounts.map((account) =>
|
||||
<AccountContainer key={`${account.id}-${account.reaction}`} id={account.id} withNote={false} reaction={account.reaction} />,
|
||||
|
|
|
@ -595,7 +595,8 @@
|
|||
"mfa.setup_warning": "𐑮𐑲𐑑 𐑞𐑰𐑟 𐑒𐑴𐑛𐑟 𐑛𐑬𐑯 𐑹 𐑕𐑱𐑝 𐑞𐑧𐑥 𐑕𐑳𐑥𐑢𐑺 𐑕𐑦𐑒𐑘𐑫𐑼 - 𐑳𐑞𐑼𐑢𐑲𐑟 𐑿 𐑢𐑴𐑯𐑑 𐑕𐑰 𐑞𐑧𐑥 𐑩𐑜𐑱𐑯. 𐑦𐑓 𐑿 𐑤𐑵𐑟 𐑨𐑒𐑕𐑧𐑕 𐑑 𐑘𐑹 2FA 𐑨𐑐 𐑯 𐑮𐑦𐑒𐑳𐑝𐑼𐑦 𐑒𐑴𐑛𐑟 𐑿𐑤 𐑚𐑰 𐑤𐑪𐑒𐑑 𐑬𐑑 𐑝 𐑘𐑹 𐑩𐑒𐑬𐑯𐑑.",
|
||||
"missing_description_modal.cancel": "𐑒𐑨𐑯𐑕𐑩𐑤",
|
||||
"missing_description_modal.continue": "𐑐𐑴𐑕𐑑",
|
||||
"missing_description_modal.text": "𐑿 𐑣𐑨𐑝 𐑯𐑪𐑑 𐑧𐑯𐑑𐑼𐑛 𐑩 𐑛𐑦𐑕𐑒𐑮𐑦𐑐𐑖𐑩𐑯 𐑓 𐑷𐑤 𐑩𐑑𐑨𐑗𐑥𐑩𐑯𐑑𐑕. 𐑒𐑩𐑯𐑑𐑦𐑯𐑿 𐑧𐑯𐑦𐑢𐑱?",
|
||||
"missing_description_modal.description": "𐑒𐑩𐑯𐑑𐑦𐑯𐑿 𐑧𐑯𐑦𐑢𐑱?",
|
||||
"missing_description_modal.text": "𐑿 𐑣𐑨𐑝 𐑯𐑪𐑑 𐑧𐑯𐑑𐑼𐑛 𐑩 𐑛𐑦𐑕𐑒𐑮𐑦𐑐𐑖𐑩𐑯 𐑓 𐑷𐑤 𐑩𐑑𐑨𐑗𐑥𐑩𐑯𐑑𐑕.",
|
||||
"missing_indicator.label": "𐑯𐑪𐑑 𐑓𐑬𐑯𐑛",
|
||||
"missing_indicator.sublabel": "𐑞𐑦𐑕 𐑮𐑦𐑟𐑹𐑕 𐑒𐑫𐑛 𐑯𐑪𐑑 𐑚𐑰 𐑓𐑬𐑯𐑛",
|
||||
"morefollows.followers_label": "…𐑯 {count} 𐑥𐑹 {count, plural, one {follower} other {followers}} 𐑪𐑯 𐑮𐑦𐑥𐑴𐑑 𐑕𐑲𐑑𐑕.",
|
||||
|
|
|
@ -598,7 +598,8 @@
|
|||
"mfa.setup_warning": "רשום את הקודים האלה או שמור אותם במקום מאובטח - אחרת לא תראה אותם שוב. אם תאבד את הגישה לאפליקציית 2FA ולקודי השחזור שלך תינעל מחוץ לחשבון שלך.",
|
||||
"missing_description_modal.cancel": "בטל",
|
||||
"missing_description_modal.continue": "שלח",
|
||||
"missing_description_modal.text": "לא הזנת תיאור עבור כל הקבצים המצורפים. המשך בכל זאת?",
|
||||
"missing_description_modal.description": "המשך בכל זאת?",
|
||||
"missing_description_modal.text": "לא הזנת תיאור עבור כל הקבצים המצורפים.",
|
||||
"missing_indicator.label": "לא נמצא",
|
||||
"missing_indicator.sublabel": "משאב זה לא נמצא",
|
||||
"morefollows.followers_label": "ו-{count} עוד {count, plural, one {עוקב} other {עוקבים}} באתרים מרוחקים.",
|
||||
|
|
|
@ -640,7 +640,8 @@
|
|||
"migration.submit": "Przenieś obserwujących",
|
||||
"missing_description_modal.cancel": "Anuluj",
|
||||
"missing_description_modal.continue": "Opublikuj",
|
||||
"missing_description_modal.text": "Nie podałeś(-aś) opisu dla wszystkich załączników. Czy na pewno chcesz kontynuować?",
|
||||
"missing_description_modal.description": "Czy na pewno chcesz kontynuować?",
|
||||
"missing_description_modal.text": "Nie podałeś(-aś) opisu dla wszystkich załączników.",
|
||||
"missing_indicator.label": "Nie znaleziono",
|
||||
"missing_indicator.sublabel": "Nie można odnaleźć tego zasobu",
|
||||
"morefollows.followers_label": "…i {count} więcej {count, plural, one {obserwujący(-a)} few {obserwujących} many {obserwujących} other {obserwujących}} na zdalnych stronach.",
|
||||
|
|
|
@ -595,7 +595,8 @@
|
|||
"mfa.setup_warning": "请立即将恢复代码保存或写到纸上,否则你可能无法登录帐号。",
|
||||
"missing_description_modal.cancel": "取消",
|
||||
"missing_description_modal.continue": "发布",
|
||||
"missing_description_modal.text": "附件没有描述信息,仍然继续发布吗?",
|
||||
"missing_description_modal.description": "仍然继续发布吗?",
|
||||
"missing_description_modal.text": "附件没有描述信息。",
|
||||
"missing_indicator.label": "找不到内容",
|
||||
"missing_indicator.sublabel": "无法找到此资源",
|
||||
"morefollows.followers_label": "和{count} 来自其他站点的 {count, plural, one {关注者} other {关注者}} 。",
|
||||
|
|
Ładowanie…
Reference in New Issue