mirror of https://framagit.org/tykayn/mastodon.git
⚡ fix account admin info, and links in the footer on the left
This commit is contained in:
parent
626fa25f4b
commit
4e336c8e6b
|
@ -49,10 +49,13 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
onKeyDown : PropTypes.func,
|
||||
onPaste : PropTypes.func.isRequired,
|
||||
autoFocus : PropTypes.bool,
|
||||
directMessage : PropTypes.bool,
|
||||
directMessageRecipient : PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
autoFocus : true,
|
||||
directMessage: false,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -75,7 +78,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this.props.onChange(e);
|
||||
}
|
||||
};
|
||||
|
||||
onKeyDown = (e) => {
|
||||
const { suggestions, disabled } = this.props;
|
||||
|
@ -124,7 +127,6 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
e.stopPropagation();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -133,25 +135,25 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this.props.onKeyDown(e);
|
||||
}
|
||||
};
|
||||
|
||||
onBlur = () => {
|
||||
this.setState({ suggestionsHidden: true, focused: false });
|
||||
}
|
||||
};
|
||||
|
||||
onFocus = (e) => {
|
||||
this.setState({ focused: true });
|
||||
if (this.props.onFocus) {
|
||||
this.props.onFocus(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionClick = (e) => {
|
||||
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
|
||||
e.preventDefault();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
|
||||
this.textarea.focus();
|
||||
}
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
|
||||
|
@ -161,14 +163,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
|
||||
setTextarea = (c) => {
|
||||
this.textarea = c;
|
||||
}
|
||||
};
|
||||
|
||||
onPaste = (e) => {
|
||||
if (e.clipboardData && e.clipboardData.files.length === 1) {
|
||||
this.props.onPaste(e.clipboardData.files);
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderSuggestion = (suggestion, i) => {
|
||||
const { selectedSuggestion } = this.state;
|
||||
|
@ -186,11 +188,18 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<div role='button' tabIndex='0' key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
|
||||
<div
|
||||
role='button'
|
||||
tabIndex='0'
|
||||
key={key}
|
||||
data-index={i}
|
||||
className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })}
|
||||
onMouseDown={this.onSuggestionClick}
|
||||
>
|
||||
{inner}
|
||||
</div >
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props;
|
||||
|
@ -202,7 +211,10 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
return [
|
||||
<div className='compose-form__autosuggest-wrapper' key='autosuggest-wrapper'>
|
||||
<div
|
||||
className='compose-form__autosuggest-wrapper'
|
||||
key='autosuggest-wrapper'
|
||||
>
|
||||
<div className='autosuggest-textarea'>
|
||||
<label >
|
||||
<span style={{ display: 'none' }}>{placeholder}</span >
|
||||
|
@ -228,8 +240,13 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
{children}
|
||||
</div >,
|
||||
|
||||
<div className='autosuggest-textarea__suggestions-wrapper' key='suggestions-wrapper'>
|
||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
|
||||
<div
|
||||
className='autosuggest-textarea__suggestions-wrapper'
|
||||
key='suggestions-wrapper'
|
||||
>
|
||||
<div
|
||||
className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}
|
||||
>
|
||||
{suggestions.map(this.renderSuggestion)}
|
||||
</div >
|
||||
</div >,
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { createPortal } from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -46,39 +46,39 @@ class ColumnHeader extends React.PureComponent {
|
|||
} else {
|
||||
this.context.router.history.goBack();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleToggleClick = (e) => {
|
||||
e.stopPropagation();
|
||||
this.setState({ collapsed: !this.state.collapsed, animating: true });
|
||||
}
|
||||
};
|
||||
|
||||
handleTitleClick = () => {
|
||||
this.props.onClick();
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveLeft = () => {
|
||||
this.props.onMove(-1);
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveRight = () => {
|
||||
this.props.onMove(1);
|
||||
}
|
||||
};
|
||||
|
||||
handleBackClick = () => {
|
||||
this.historyBack();
|
||||
}
|
||||
};
|
||||
|
||||
handleTransitionEnd = () => {
|
||||
this.setState({ animating: false });
|
||||
}
|
||||
};
|
||||
|
||||
handlePin = () => {
|
||||
if (!this.props.pinned) {
|
||||
this.historyBack();
|
||||
}
|
||||
this.props.onPin();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder } = this.props;
|
||||
|
@ -105,30 +105,81 @@ class ColumnHeader extends React.PureComponent {
|
|||
|
||||
if (children) {
|
||||
extraContent = (
|
||||
<div key='extra-content' className='column-header__collapsible__extra'>
|
||||
<div
|
||||
key='extra-content'
|
||||
className='column-header__collapsible__extra'
|
||||
>
|
||||
{children}
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
if (multiColumn && pinned) {
|
||||
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='times' /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>;
|
||||
pinButton =
|
||||
(<button
|
||||
key='pin-button'
|
||||
className='text-btn column-header__setting-btn'
|
||||
onClick={this.handlePin}
|
||||
><Icon
|
||||
id='times'
|
||||
/> <FormattedMessage
|
||||
id='column_header.unpin'
|
||||
defaultMessage='Unpin'
|
||||
/></button >);
|
||||
|
||||
moveButtons = (
|
||||
<div key='move-buttons' className='column-header__setting-arrows'>
|
||||
<button title={formatMessage(messages.moveLeft)} aria-label={formatMessage(messages.moveLeft)} className='text-btn column-header__setting-btn' onClick={this.handleMoveLeft}><Icon id='chevron-left' /></button>
|
||||
<button title={formatMessage(messages.moveRight)} aria-label={formatMessage(messages.moveRight)} className='text-btn column-header__setting-btn' onClick={this.handleMoveRight}><Icon id='chevron-right' /></button>
|
||||
<div
|
||||
key='move-buttons'
|
||||
className='column-header__setting-arrows'
|
||||
>
|
||||
<button
|
||||
title={formatMessage(messages.moveLeft)}
|
||||
aria-label={formatMessage(messages.moveLeft)}
|
||||
className='text-btn column-header__setting-btn'
|
||||
onClick={this.handleMoveLeft}
|
||||
><Icon
|
||||
id='chevron-left'
|
||||
/></button >
|
||||
<button
|
||||
title={formatMessage(messages.moveRight)}
|
||||
aria-label={formatMessage(messages.moveRight)}
|
||||
className='text-btn column-header__setting-btn'
|
||||
onClick={this.handleMoveRight}
|
||||
><Icon
|
||||
id='chevron-right'
|
||||
/></button >
|
||||
</div >
|
||||
);
|
||||
} else if (multiColumn && this.props.onPin) {
|
||||
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
|
||||
pinButton =
|
||||
(<button
|
||||
key='pin-button'
|
||||
className='text-btn column-header__setting-btn'
|
||||
onClick={this.handlePin}
|
||||
><Icon
|
||||
id='plus'
|
||||
/> <FormattedMessage
|
||||
id='column_header.pin'
|
||||
defaultMessage='Pin'
|
||||
/>
|
||||
</button >);
|
||||
}
|
||||
|
||||
if (!pinned && (multiColumn || showBackButton)) {
|
||||
backButton = (
|
||||
<button onClick={this.handleBackClick} className='column-header__back-button'>
|
||||
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
|
||||
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||
<button
|
||||
onClick={this.handleBackClick}
|
||||
className='column-header__back-button'
|
||||
>
|
||||
<Icon
|
||||
id='chevron-left'
|
||||
className='column-back-button__icon'
|
||||
fixedWidth
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='column_back_button.label'
|
||||
defaultMessage='Back'
|
||||
/>
|
||||
</button >
|
||||
);
|
||||
}
|
||||
|
@ -143,7 +194,16 @@ class ColumnHeader extends React.PureComponent {
|
|||
}
|
||||
|
||||
if (children || (multiColumn && this.props.onPin)) {
|
||||
collapseButton = <button className={collapsibleButtonClassName} title={formatMessage(collapsed ? messages.show : messages.hide)} aria-label={formatMessage(collapsed ? messages.show : messages.hide)} aria-pressed={collapsed ? 'false' : 'true'} onClick={this.handleToggleClick}><Icon id='sliders' /></button>;
|
||||
collapseButton =
|
||||
(<button
|
||||
className={collapsibleButtonClassName}
|
||||
title={formatMessage(collapsed ? messages.show : messages.hide)}
|
||||
aria-label={formatMessage(collapsed ? messages.show : messages.hide)}
|
||||
aria-pressed={collapsed ? 'false' : 'true'}
|
||||
onClick={this.handleToggleClick}
|
||||
>
|
||||
<Icon id='sliders' />
|
||||
</button >);
|
||||
}
|
||||
|
||||
const hasTitle = icon && title;
|
||||
|
@ -153,7 +213,11 @@ class ColumnHeader extends React.PureComponent {
|
|||
<h1 className={buttonClassName}>
|
||||
{hasTitle && (
|
||||
<button onClick={this.handleTitleClick}>
|
||||
<Icon id={icon} fixedWidth className='column-header__icon' />
|
||||
<Icon
|
||||
id={icon}
|
||||
fixedWidth
|
||||
className='column-header__icon'
|
||||
/>
|
||||
{title}
|
||||
</button >
|
||||
)}
|
||||
|
@ -167,7 +231,11 @@ class ColumnHeader extends React.PureComponent {
|
|||
</div >
|
||||
</h1 >
|
||||
|
||||
<div className={collapsibleClassName} tabIndex={collapsed ? -1 : null} onTransitionEnd={this.handleTransitionEnd}>
|
||||
<div
|
||||
className={collapsibleClassName}
|
||||
tabIndex={collapsed ? -1 : null}
|
||||
onTransitionEnd={this.handleTransitionEnd}
|
||||
>
|
||||
<div className='column-header__collapsible-inner'>
|
||||
{(!collapsed || animating) && collapsedContent}
|
||||
</div >
|
||||
|
|
|
@ -42,15 +42,15 @@ export default class DisplayName extends React.PureComponent {
|
|||
|
||||
handleEmojiMouseEnter = ({ target }) => {
|
||||
target.src = target.getAttribute('data-original');
|
||||
}
|
||||
};
|
||||
|
||||
handleEmojiMouseLeave = ({ target }) => {
|
||||
target.src = target.getAttribute('data-static');
|
||||
}
|
||||
};
|
||||
|
||||
setRef = (c) => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { others, localDomain } = this.props;
|
||||
|
@ -58,7 +58,14 @@ export default class DisplayName extends React.PureComponent {
|
|||
let displayName, suffix, 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]);
|
||||
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}`;
|
||||
|
@ -76,12 +83,20 @@ export default class DisplayName extends React.PureComponent {
|
|||
acct = `${acct}@${localDomain}`;
|
||||
}
|
||||
|
||||
displayName = <bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi>;
|
||||
displayName = <bdi ><strong
|
||||
className='display-name__html'
|
||||
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
|
||||
/>
|
||||
|
||||
</bdi >;
|
||||
suffix = <span className='display-name__account'>@{acct}</span >;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className='display-name' ref={this.setRef}>
|
||||
<span
|
||||
className='display-name'
|
||||
ref={this.setRef}
|
||||
>
|
||||
{displayName} {suffix}
|
||||
</span >
|
||||
);
|
||||
|
|
|
@ -10,17 +10,17 @@ import StatusContent from './status_content';
|
|||
import StatusActionBar from './status_action_bar';
|
||||
import AttachmentList from './attachment_list';
|
||||
import Card from '../features/status/components/card';
|
||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { MediaGallery, Video, Audio } from '../features/ui/util/async-components';
|
||||
import { Audio, MediaGallery, Video } from '../features/ui/util/async-components';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import classNames from 'classnames';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import { displayMedia } from '../initial_state';
|
||||
|
||||
// We use the component (and not the container) since we do not want
|
||||
// to use the progress bar to show download progress
|
||||
import Bundle from '../features/ui/components/bundle';
|
||||
import imageShowThread from '../../images/icon_reply.svg';
|
||||
|
||||
export const textForScreenReader = (intl, status, rebloggedByText = false) => {
|
||||
const displayName = status.getIn(['account', 'display_name']);
|
||||
|
@ -102,6 +102,17 @@ class Status extends ImmutablePureComponent {
|
|||
statusId : undefined,
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
|
||||
return {
|
||||
showMedia: defaultMediaVisibility(nextProps.status),
|
||||
statusId : nextProps.status.get('id'),
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Track height changes we know about to compensate scrolling
|
||||
componentDidMount() {
|
||||
this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
|
||||
|
@ -115,17 +126,6 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
|
||||
return {
|
||||
showMedia: defaultMediaVisibility(nextProps.status),
|
||||
statusId: nextProps.status.get('id'),
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Compensate height changes
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
const doShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
|
||||
|
@ -154,7 +154,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleToggleMediaVisibility = () => {
|
||||
this.setState({ showMedia: !this.state.showMedia });
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
if (this.props.onClick) {
|
||||
|
@ -168,7 +168,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
const { status } = this.props;
|
||||
this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleExpandClick = (e) => {
|
||||
if (this.props.onClick) {
|
||||
|
@ -184,7 +184,7 @@ class Status extends ImmutablePureComponent {
|
|||
const { status } = this.props;
|
||||
this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleAccountClick = (e) => {
|
||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
|
@ -192,27 +192,36 @@ class Status extends ImmutablePureComponent {
|
|||
e.preventDefault();
|
||||
this.context.router.history.push(`/accounts/${id}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleExpandedToggle = () => {
|
||||
this.props.onToggleHidden(this._properStatus());
|
||||
};
|
||||
|
||||
renderLoadingMediaGallery() {
|
||||
return <div className='media-gallery' style={{ height: '110px' }} />;
|
||||
return <div
|
||||
className='media-gallery'
|
||||
style={{ height: '110px' }}
|
||||
/>;
|
||||
}
|
||||
|
||||
renderLoadingVideoPlayer() {
|
||||
return <div className='video-player' style={{ height: '110px' }} />;
|
||||
return <div
|
||||
className='video-player'
|
||||
style={{ height: '110px' }}
|
||||
/>;
|
||||
}
|
||||
|
||||
renderLoadingAudioPlayer() {
|
||||
return <div className='audio-player' style={{ height: '110px' }} />;
|
||||
return <div
|
||||
className='audio-player'
|
||||
style={{ height: '110px' }}
|
||||
/>;
|
||||
}
|
||||
|
||||
handleOpenVideo = (media, startTime) => {
|
||||
this.props.onOpenVideo(media, startTime);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpenMedia = e => {
|
||||
const { onOpenMedia, onOpenVideo } = this.props;
|
||||
|
@ -229,49 +238,49 @@ class Status extends ImmutablePureComponent {
|
|||
onOpenMedia(status.get('media_attachments'), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyReply = e => {
|
||||
e.preventDefault();
|
||||
this.props.onReply(this._properStatus(), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyFavourite = () => {
|
||||
this.props.onFavourite(this._properStatus());
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyBoost = e => {
|
||||
this.props.onReblog(this._properStatus(), e);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMention = e => {
|
||||
e.preventDefault();
|
||||
this.props.onMention(this._properStatus().get('account'), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpen = () => {
|
||||
this.context.router.history.push(`/statuses/${this._properStatus().get('id')}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpenProfile = () => {
|
||||
this.context.router.history.push(`/accounts/${this._properStatus().getIn(['account', 'id'])}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveUp = e => {
|
||||
this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveDown = e => {
|
||||
this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleHidden = () => {
|
||||
this.props.onToggleHidden(this._properStatus());
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleSensitive = () => {
|
||||
this.handleToggleMediaVisibility();
|
||||
}
|
||||
};
|
||||
|
||||
_properStatus() {
|
||||
const { status } = this.props;
|
||||
|
@ -285,7 +294,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let media = null;
|
||||
|
@ -316,7 +325,11 @@ class Status extends ImmutablePureComponent {
|
|||
if (hidden) {
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div ref={this.handleRef} className={classNames('status__wrapper', { focusable: !this.props.muted })} tabIndex='0'>
|
||||
<div
|
||||
ref={this.handleRef}
|
||||
className={classNames('status__wrapper', { focusable: !this.props.muted })}
|
||||
tabIndex='0'
|
||||
>
|
||||
{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}
|
||||
{status.get('content')}
|
||||
</div >
|
||||
|
@ -332,8 +345,15 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<HotKeys handlers={minHandlers}>
|
||||
<div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0' ref={this.handleRef}>
|
||||
<FormattedMessage id='status.filtered' defaultMessage='Filtered' />
|
||||
<div
|
||||
className='status__wrapper status__wrapper--filtered focusable'
|
||||
tabIndex='0'
|
||||
ref={this.handleRef}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='status.filtered'
|
||||
defaultMessage='Filtered'
|
||||
/>
|
||||
</div >
|
||||
</HotKeys >
|
||||
);
|
||||
|
@ -342,8 +362,15 @@ class Status extends ImmutablePureComponent {
|
|||
if (featured) {
|
||||
prepend = (
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='thumb-tack' className='status__prepend-icon' fixedWidth /></div>
|
||||
<FormattedMessage id='status.pinned' defaultMessage='Pinned toot' />
|
||||
<div className='status__prepend-icon-wrapper'><Icon
|
||||
id='thumb-tack'
|
||||
className='status__prepend-icon'
|
||||
fixedWidth
|
||||
/></div >
|
||||
<FormattedMessage
|
||||
id='status.pinned'
|
||||
defaultMessage='Pinned toot'
|
||||
/>
|
||||
</div >
|
||||
);
|
||||
} else if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||
|
@ -351,12 +378,33 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
prepend = (
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='retweet' className='status__prepend-icon' fixedWidth /></div>
|
||||
<FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} />
|
||||
<div className='status__prepend-icon-wrapper'><Icon
|
||||
id='retweet'
|
||||
className='status__prepend-icon'
|
||||
fixedWidth
|
||||
/>
|
||||
</div >
|
||||
<FormattedMessage
|
||||
id='status.reblogged_by'
|
||||
defaultMessage='{name} boosted'
|
||||
values={{
|
||||
name: <a
|
||||
onClick={this.handleAccountClick}
|
||||
data-id={status.getIn(['account', 'id'])}
|
||||
href={status.getIn(['account', 'url'])}
|
||||
className='status__display-name muted'
|
||||
>
|
||||
<bdi ><strong dangerouslySetInnerHTML={display_name_html} /></bdi >
|
||||
</a >,
|
||||
}}
|
||||
/>
|
||||
</div >
|
||||
);
|
||||
|
||||
rebloggedByText = intl.formatMessage({ id: 'status.reblogged_by', defaultMessage: '{name} boosted' }, { name: status.getIn(['account', 'acct']) });
|
||||
rebloggedByText = intl.formatMessage({
|
||||
id : 'status.reblogged_by',
|
||||
defaultMessage: '{name} boosted',
|
||||
}, { name: status.getIn(['account', 'acct']) });
|
||||
|
||||
account = status.get('account');
|
||||
status = status.get('reblog');
|
||||
|
@ -374,7 +422,10 @@ class Status extends ImmutablePureComponent {
|
|||
const attachment = status.getIn(['media_attachments', 0]);
|
||||
|
||||
media = (
|
||||
<Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} >
|
||||
<Bundle
|
||||
fetchComponent={Audio}
|
||||
loading={this.renderLoadingAudioPlayer}
|
||||
>
|
||||
{Component => (
|
||||
<Component
|
||||
src={attachment.get('url')}
|
||||
|
@ -390,7 +441,10 @@ class Status extends ImmutablePureComponent {
|
|||
const attachment = status.getIn(['media_attachments', 0]);
|
||||
|
||||
media = (
|
||||
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
|
||||
<Bundle
|
||||
fetchComponent={Video}
|
||||
loading={this.renderLoadingVideoPlayer}
|
||||
>
|
||||
{Component => (
|
||||
<Component
|
||||
preview={attachment.get('preview_url')}
|
||||
|
@ -411,7 +465,10 @@ class Status extends ImmutablePureComponent {
|
|||
);
|
||||
} else {
|
||||
media = (
|
||||
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
|
||||
<Bundle
|
||||
fetchComponent={MediaGallery}
|
||||
loading={this.renderLoadingMediaGallery}
|
||||
>
|
||||
{Component => (
|
||||
<Component
|
||||
media={status.get('media_attachments')}
|
||||
|
@ -440,43 +497,108 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (otherAccounts && otherAccounts.size > 0) {
|
||||
statusAvatar = <AvatarComposite accounts={otherAccounts} size={48} />;
|
||||
statusAvatar = <AvatarComposite
|
||||
accounts={otherAccounts}
|
||||
size={48}
|
||||
/>;
|
||||
} else if (account === undefined || account === null) {
|
||||
statusAvatar = <Avatar account={status.get('account')} size={48} />;
|
||||
statusAvatar = <Avatar
|
||||
account={status.get('account')}
|
||||
size={48}
|
||||
/>;
|
||||
} else {
|
||||
statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />;
|
||||
statusAvatar = <AvatarOverlay
|
||||
account={status.get('account')}
|
||||
friend={account}
|
||||
/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
|
||||
<div
|
||||
className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, {
|
||||
'status__wrapper-reply': !!status.get('in_reply_to_id'),
|
||||
read : unread === false,
|
||||
focusable : !this.props.muted,
|
||||
})}
|
||||
tabIndex={this.props.muted ? null : 0}
|
||||
data-featured={featured ? 'true' : null}
|
||||
aria-label={textForScreenReader(intl, status, rebloggedByText)}
|
||||
ref={this.handleRef}
|
||||
>
|
||||
{prepend}
|
||||
|
||||
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted, read: unread === false })} data-id={status.get('id')}>
|
||||
<div className='status__expand' onClick={this.handleExpandClick} role='presentation' />
|
||||
<div
|
||||
className={classNames('status', `status-${status.get('visibility')}`, {
|
||||
'status-reply': !!status.get('in_reply_to_id'),
|
||||
muted : this.props.muted,
|
||||
read : unread === false,
|
||||
})}
|
||||
data-id={status.get('id')}
|
||||
>
|
||||
<div
|
||||
className='status__expand'
|
||||
onClick={this.handleExpandClick}
|
||||
role='presentation'
|
||||
/>
|
||||
<div className='status__info'>
|
||||
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
|
||||
<a
|
||||
href={status.get('url')}
|
||||
className='status__relative-time'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
><RelativeTimestamp timestamp={status.get('created_at')} /></a >
|
||||
|
||||
<a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} title={status.getIn(['account', 'acct'])} className='status__display-name' target='_blank' rel='noopener noreferrer'>
|
||||
<a
|
||||
onClick={this.handleAccountClick}
|
||||
data-id={status.getIn(['account', 'id'])}
|
||||
href={status.getIn(['account', 'url'])}
|
||||
title={status.getIn(['account', 'acct'])}
|
||||
className='status__display-name'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<div className='status__avatar'>
|
||||
{statusAvatar}
|
||||
</div >
|
||||
|
||||
<DisplayName account={status.get('account')} others={otherAccounts} />
|
||||
<DisplayName
|
||||
account={status.get('account')}
|
||||
others={otherAccounts}
|
||||
/>
|
||||
</a >
|
||||
</div >
|
||||
|
||||
<StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} collapsable />
|
||||
<StatusContent
|
||||
status={status}
|
||||
onClick={this.handleClick}
|
||||
expanded={!status.get('hidden')}
|
||||
onExpandedToggle={this.handleExpandedToggle}
|
||||
collapsable
|
||||
/>
|
||||
|
||||
{media}
|
||||
|
||||
{showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) && (
|
||||
<button className='status__content__read-more-button' onClick={this.handleClick}>
|
||||
<FormattedMessage id='status.show_thread' defaultMessage='Show thread' />
|
||||
<button
|
||||
className='status__content__read-more-button'
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
|
||||
<FormattedMessage
|
||||
id='status.show_thread'
|
||||
defaultMessage='Show thread'
|
||||
/>
|
||||
<img
|
||||
src={imageShowThread}
|
||||
alt='=> '
|
||||
/>
|
||||
</button >
|
||||
)}
|
||||
|
||||
<StatusActionBar status={status} account={account} {...other} />
|
||||
<StatusActionBar
|
||||
status={status}
|
||||
account={account} {...other} />
|
||||
</div >
|
||||
</div >
|
||||
</HotKeys >
|
||||
|
|
|
@ -5,7 +5,7 @@ import IconButton from '../../../components/icon_button';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { me, isStaff } from '../../../initial_state';
|
||||
import { isStaff, me } from '../../../initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
delete : { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
|
@ -74,35 +74,35 @@ class ActionBar extends React.PureComponent {
|
|||
|
||||
handleReplyClick = () => {
|
||||
this.props.onReply(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleReblogClick = (e) => {
|
||||
this.props.onReblog(this.props.status, e);
|
||||
}
|
||||
};
|
||||
|
||||
handleFavouriteClick = () => {
|
||||
this.props.onFavourite(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleBookmarkClick = (e) => {
|
||||
this.props.onBookmark(this.props.status, e);
|
||||
}
|
||||
};
|
||||
|
||||
handleDeleteClick = () => {
|
||||
this.props.onDelete(this.props.status, this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleRedraftClick = () => {
|
||||
this.props.onDelete(this.props.status, this.context.router.history, true);
|
||||
}
|
||||
};
|
||||
|
||||
handleDirectClick = () => {
|
||||
this.props.onDirect(this.props.status.get('account'), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleMentionClick = () => {
|
||||
this.props.onMention(this.props.status.get('account'), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleMuteClick = () => {
|
||||
const { status, relationship, onMute, onUnmute } = this.props;
|
||||
|
@ -113,7 +113,7 @@ class ActionBar extends React.PureComponent {
|
|||
} else {
|
||||
onMute(account);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBlockClick = () => {
|
||||
const { status, relationship, onBlock, onUnblock } = this.props;
|
||||
|
@ -124,44 +124,44 @@ class ActionBar extends React.PureComponent {
|
|||
} else {
|
||||
onBlock(status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBlockDomain = () => {
|
||||
const { status, onBlockDomain } = this.props;
|
||||
const account = status.get('account');
|
||||
|
||||
onBlockDomain(account.get('acct').split('@')[1]);
|
||||
}
|
||||
};
|
||||
|
||||
handleUnblockDomain = () => {
|
||||
const { status, onUnblockDomain } = this.props;
|
||||
const account = status.get('account');
|
||||
|
||||
onUnblockDomain(account.get('acct').split('@')[1]);
|
||||
}
|
||||
};
|
||||
|
||||
handleConversationMuteClick = () => {
|
||||
this.props.onMuteConversation(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleReport = () => {
|
||||
this.props.onReport(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handlePinClick = () => {
|
||||
this.props.onPin(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleShare = () => {
|
||||
navigator.share({
|
||||
text: this.props.status.get('search_index'),
|
||||
url : this.props.status.get('url'),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleEmbed = () => {
|
||||
this.props.onEmbed(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleCopy = () => {
|
||||
const url = this.props.status.get('url');
|
||||
|
@ -180,7 +180,7 @@ class ActionBar extends React.PureComponent {
|
|||
} finally {
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { status, relationship, intl } = this.props;
|
||||
|
@ -199,36 +199,66 @@ class ActionBar extends React.PureComponent {
|
|||
|
||||
if (me === status.getIn(['account', 'id'])) {
|
||||
if (publicStatus) {
|
||||
menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin),
|
||||
action: this.handlePinClick,
|
||||
});
|
||||
} else {
|
||||
if (status.get('visibility') === 'private') {
|
||||
menu.push({ text: intl.formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleReblogClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private),
|
||||
action: this.handleReblogClick,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation),
|
||||
action: this.handleConversationMuteClick,
|
||||
});
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
|
||||
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
|
||||
} else {
|
||||
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
||||
menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }),
|
||||
action: this.handleMentionClick,
|
||||
});
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }),
|
||||
action: this.handleDirectClick,
|
||||
});
|
||||
menu.push(null);
|
||||
|
||||
if (relationship && relationship.get('muting')) {
|
||||
menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.unmute, { name: account.get('username') }),
|
||||
action: this.handleMuteClick,
|
||||
});
|
||||
} else {
|
||||
menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.mute, { name: account.get('username') }),
|
||||
action: this.handleMuteClick,
|
||||
});
|
||||
}
|
||||
|
||||
if (relationship && relationship.get('blocking')) {
|
||||
menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.unblock, { name: account.get('username') }),
|
||||
action: this.handleBlockClick,
|
||||
});
|
||||
} else {
|
||||
menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.block, { name: account.get('username') }),
|
||||
action: this.handleBlockClick,
|
||||
});
|
||||
}
|
||||
|
||||
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }),
|
||||
action: this.handleReport,
|
||||
});
|
||||
|
||||
if (account.get('acct') !== account.get('username')) {
|
||||
const domain = account.get('acct').split('@')[1];
|
||||
|
@ -244,13 +274,23 @@ class ActionBar extends React.PureComponent {
|
|||
|
||||
if (isStaff) {
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
|
||||
menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }),
|
||||
href: `/admin/accounts/${status.getIn(['account', 'id'])}`,
|
||||
});
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.admin_status),
|
||||
href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const shareButton = ('share' in navigator) && status.get('visibility') === 'public' && (
|
||||
<div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.share)} icon='share-alt' onClick={this.handleShare} /></div>
|
||||
<div className='detailed-status__button'><IconButton
|
||||
title={intl.formatMessage(messages.share)}
|
||||
icon='share-alt'
|
||||
onClick={this.handleShare}
|
||||
/></div >
|
||||
);
|
||||
|
||||
let replyIcon;
|
||||
|
@ -268,14 +308,46 @@ class ActionBar extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<div className='detailed-status__action-bar'>
|
||||
<div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton disabled={reblog_disabled} active={status.get('reblogged')} title={reblog_disabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)} icon={reblogIcon} onClick={this.handleReblogClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton
|
||||
title={intl.formatMessage(messages.reply)}
|
||||
icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon}
|
||||
onClick={this.handleReplyClick}
|
||||
/></div >
|
||||
<div className='detailed-status__button'><IconButton
|
||||
disabled={reblog_disabled}
|
||||
active={status.get('reblogged')}
|
||||
title={reblog_disabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)}
|
||||
icon={reblogIcon}
|
||||
onClick={this.handleReblogClick}
|
||||
/></div >
|
||||
<div className='detailed-status__button'>
|
||||
<IconButton
|
||||
className='star-icon'
|
||||
animate
|
||||
active={status.get('favourited')}
|
||||
title={intl.formatMessage(messages.favourite)}
|
||||
icon='star'
|
||||
onClick={this.handleFavouriteClick}
|
||||
/></div >
|
||||
{shareButton}
|
||||
<div className='detailed-status__button'><IconButton className='bookmark-icon' active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /></div>
|
||||
<div className='detailed-status__button'>
|
||||
<IconButton
|
||||
className='bookmark-icon'
|
||||
active={status.get('bookmarked')}
|
||||
title={intl.formatMessage(messages.bookmark)}
|
||||
icon='bookmark'
|
||||
onClick={this.handleBookmarkClick}
|
||||
/></div >
|
||||
|
||||
<div className='detailed-status__action-bar-dropdown'>
|
||||
<DropdownMenuContainer size={18} icon='ellipsis-h' status={status} items={menu} direction='left' title='More' />
|
||||
<DropdownMenuContainer
|
||||
size={18}
|
||||
icon='ellipsis-h'
|
||||
status={status}
|
||||
items={menu}
|
||||
direction='left'
|
||||
title='More'
|
||||
/>
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
|
|
|
@ -5,41 +5,24 @@ import PropTypes from 'prop-types';
|
|||
import classNames from 'classnames';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchStatus } from '../../actions/statuses';
|
||||
import { deleteStatus, fetchStatus, hideStatus, muteStatus, revealStatus, unmuteStatus } from '../../actions/statuses';
|
||||
import MissingIndicator from '../../components/missing_indicator';
|
||||
import DetailedStatus from './components/detailed_status';
|
||||
import ActionBar from './components/action_bar';
|
||||
import Column from '../ui/components/column';
|
||||
import {
|
||||
favourite,
|
||||
unfavourite,
|
||||
bookmark,
|
||||
unbookmark,
|
||||
reblog,
|
||||
unreblog,
|
||||
favourite,
|
||||
pin,
|
||||
reblog,
|
||||
unbookmark,
|
||||
unfavourite,
|
||||
unpin,
|
||||
unreblog,
|
||||
} from '../../actions/interactions';
|
||||
import {
|
||||
replyCompose,
|
||||
mentionCompose,
|
||||
directCompose,
|
||||
} from '../../actions/compose';
|
||||
import {
|
||||
muteStatus,
|
||||
unmuteStatus,
|
||||
deleteStatus,
|
||||
hideStatus,
|
||||
revealStatus,
|
||||
} from '../../actions/statuses';
|
||||
import {
|
||||
unblockAccount,
|
||||
unmuteAccount,
|
||||
} from '../../actions/accounts';
|
||||
import {
|
||||
blockDomain,
|
||||
unblockDomain,
|
||||
} from '../../actions/domain_blocks';
|
||||
import { directCompose, mentionCompose, replyCompose } from '../../actions/compose';
|
||||
import { unblockAccount, unmuteAccount } from '../../actions/accounts';
|
||||
import { blockDomain, unblockDomain } from '../../actions/domain_blocks';
|
||||
import { initMuteModal } from '../../actions/mutes';
|
||||
import { initBlockModal } from '../../actions/blocks';
|
||||
import { initReport } from '../../actions/reports';
|
||||
|
@ -49,24 +32,33 @@ import ColumnBackButton from '../../components/column_back_button';
|
|||
import ColumnHeader from '../../components/column_header';
|
||||
import StatusContainer from '../../containers/status_container';
|
||||
import { openModal } from '../../actions/modal';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import { boostModal, deleteModal } from '../../initial_state';
|
||||
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen';
|
||||
import { textForScreenReader, defaultMediaVisibility } from '../../components/status';
|
||||
import { defaultMediaVisibility, textForScreenReader } from '../../components/status';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteConfirm : { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
||||
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
|
||||
deleteMessage : {
|
||||
id : 'confirmations.delete.message',
|
||||
defaultMessage: 'Are you sure you want to delete this status?',
|
||||
},
|
||||
redraftConfirm : { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
|
||||
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
|
||||
redraftMessage : {
|
||||
id : 'confirmations.redraft.message',
|
||||
defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.',
|
||||
},
|
||||
revealAll : { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
|
||||
hideAll : { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
|
||||
detailedStatus : { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
|
||||
replyConfirm : { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||
replyMessage : {
|
||||
id : 'confirmations.reply.message',
|
||||
defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?',
|
||||
},
|
||||
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
||||
});
|
||||
|
||||
|
@ -116,7 +108,8 @@ const makeMapStateToProps = () => {
|
|||
let insertAt = descendantsIds.findIndex((id) => statuses.get(id).get('in_reply_to_account_id') !== statuses.get(id).get('account'));
|
||||
if (insertAt !== -1) {
|
||||
descendantsIds.forEach((id, idx) => {
|
||||
if (idx > insertAt && statuses.get(id).get('in_reply_to_account_id') === statuses.get(id).get('account')) {
|
||||
let account = statuses.get(id).get('account');
|
||||
if (idx > insertAt && statuses.get(id).get('in_reply_to_account_id') === account) {
|
||||
descendantsIds.splice(idx, 1);
|
||||
descendantsIds.splice(insertAt, 0, id);
|
||||
insertAt += 1;
|
||||
|
@ -190,13 +183,16 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) {
|
||||
this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id') });
|
||||
this.setState({
|
||||
showMedia : defaultMediaVisibility(nextProps.status),
|
||||
loadedStatusId: nextProps.status.get('id'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleToggleMediaVisibility = () => {
|
||||
this.setState({ showMedia: !this.state.showMedia });
|
||||
}
|
||||
};
|
||||
|
||||
handleFavouriteClick = (status) => {
|
||||
if (status.get('favourited')) {
|
||||
|
@ -204,7 +200,7 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(favourite(status));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handlePin = (status) => {
|
||||
if (status.get('pinned')) {
|
||||
|
@ -212,7 +208,7 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(pin(status));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleReplyClick = (status) => {
|
||||
let { askReplyConfirmation, dispatch, intl } = this.props;
|
||||
|
@ -225,11 +221,11 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
dispatch(replyCompose(status, this.context.router.history));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleModalReblog = (status) => {
|
||||
this.props.dispatch(reblog(status));
|
||||
}
|
||||
};
|
||||
|
||||
handleReblogClick = (status, e) => {
|
||||
if (status.get('reblogged')) {
|
||||
|
@ -241,7 +237,7 @@ class Status extends ImmutablePureComponent {
|
|||
this.props.dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBookmarkClick = (status) => {
|
||||
if (status.get('bookmarked')) {
|
||||
|
@ -249,7 +245,7 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(bookmark(status));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDeleteClick = (status, history, withRedraft = false) => {
|
||||
const { dispatch, intl } = this.props;
|
||||
|
@ -263,23 +259,23 @@ class Status extends ImmutablePureComponent {
|
|||
onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDirectClick = (account, router) => {
|
||||
this.props.dispatch(directCompose(account, router));
|
||||
}
|
||||
};
|
||||
|
||||
handleMentionClick = (account, router) => {
|
||||
this.props.dispatch(mentionCompose(account, router));
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenMedia = (media, index) => {
|
||||
this.props.dispatch(openModal('MEDIA', { media, index }));
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenVideo = (media, time) => {
|
||||
this.props.dispatch(openModal('VIDEO', { media, time }));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpenMedia = e => {
|
||||
const status = this._properStatus();
|
||||
|
@ -295,11 +291,11 @@ class Status extends ImmutablePureComponent {
|
|||
this.handleOpenMedia(status.get('media_attachments'), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMuteClick = (account) => {
|
||||
this.props.dispatch(initMuteModal(account));
|
||||
}
|
||||
};
|
||||
|
||||
handleConversationMuteClick = (status) => {
|
||||
if (status.get('muted')) {
|
||||
|
@ -307,7 +303,7 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(muteStatus(status.get('id')));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleToggleHidden = (status) => {
|
||||
if (status.get('hidden')) {
|
||||
|
@ -315,7 +311,7 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(hideStatus(status.get('id')));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleToggleAll = () => {
|
||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||
|
@ -326,80 +322,83 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(hideStatus(statusIds));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBlockClick = (status) => {
|
||||
const { dispatch } = this.props;
|
||||
const account = status.get('account');
|
||||
dispatch(initBlockModal(account));
|
||||
}
|
||||
};
|
||||
|
||||
handleReport = (status) => {
|
||||
this.props.dispatch(initReport(status.get('account'), status));
|
||||
}
|
||||
};
|
||||
|
||||
handleEmbed = (status) => {
|
||||
this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
|
||||
}
|
||||
};
|
||||
|
||||
handleUnmuteClick = account => {
|
||||
this.props.dispatch(unmuteAccount(account.get('id')));
|
||||
}
|
||||
};
|
||||
|
||||
handleUnblockClick = account => {
|
||||
this.props.dispatch(unblockAccount(account.get('id')));
|
||||
}
|
||||
};
|
||||
|
||||
handleBlockDomainClick = domain => {
|
||||
this.props.dispatch(openModal('CONFIRM', {
|
||||
message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.' values={{ domain: <strong>{domain}</strong> }} />,
|
||||
message : <FormattedMessage
|
||||
id='confirmations.domain_block.message'
|
||||
defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.'
|
||||
values={{ domain: <strong >{domain}</strong > }}
|
||||
/>,
|
||||
confirm : this.props.intl.formatMessage(messages.blockDomainConfirm),
|
||||
onConfirm: () => this.props.dispatch(blockDomain(domain)),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
handleUnblockDomainClick = domain => {
|
||||
this.props.dispatch(unblockDomain(domain));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
handleHotkeyMoveUp = () => {
|
||||
this.handleMoveUp(this.props.status.get('id'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveDown = () => {
|
||||
this.handleMoveDown(this.props.status.get('id'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyReply = e => {
|
||||
e.preventDefault();
|
||||
this.handleReplyClick(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyFavourite = () => {
|
||||
this.handleFavouriteClick(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyBoost = () => {
|
||||
this.handleReblogClick(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMention = e => {
|
||||
e.preventDefault();
|
||||
this.handleMentionClick(this.props.status.get('account'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpenProfile = () => {
|
||||
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleHidden = () => {
|
||||
this.handleToggleHidden(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleSensitive = () => {
|
||||
this.handleToggleMediaVisibility();
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveUp = id => {
|
||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||
|
@ -416,7 +415,7 @@ class Status extends ImmutablePureComponent {
|
|||
this._selectChild(index - 1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveDown = id => {
|
||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||
|
@ -433,7 +432,7 @@ class Status extends ImmutablePureComponent {
|
|||
this._selectChild(index + 1, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_selectChild(index, align_top) {
|
||||
const container = this.node;
|
||||
|
@ -463,7 +462,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this._scrolledIntoView) {
|
||||
|
@ -488,7 +487,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
onFullScreenChange = () => {
|
||||
this.setState({ fullscreen: isFullscreen() });
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let ancestors, descendants;
|
||||
|
@ -526,21 +525,40 @@ class Status extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.detailedStatus)}>
|
||||
<Column
|
||||
bindToDocument={!multiColumn}
|
||||
label={intl.formatMessage(messages.detailedStatus)}
|
||||
>
|
||||
<ColumnHeader
|
||||
showBackButton
|
||||
multiColumn={multiColumn}
|
||||
extraButton={(
|
||||
<button className='column-header__button' title={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} aria-label={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} onClick={this.handleToggleAll} aria-pressed={status.get('hidden') ? 'false' : 'true'}><Icon id={status.get('hidden') ? 'eye-slash' : 'eye'} /></button>
|
||||
<button
|
||||
className='column-header__button'
|
||||
title={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)}
|
||||
aria-label={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)}
|
||||
onClick={this.handleToggleAll}
|
||||
aria-pressed={status.get('hidden') ? 'false' : 'true'}
|
||||
><Icon id={status.get('hidden') ? 'eye-slash' : 'eye'} /></button >
|
||||
)}
|
||||
/>
|
||||
|
||||
<ScrollContainer scrollKey='thread' shouldUpdateScroll={shouldUpdateScroll}>
|
||||
<div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
|
||||
<ScrollContainer
|
||||
scrollKey='thread'
|
||||
shouldUpdateScroll={shouldUpdateScroll}
|
||||
>
|
||||
<div
|
||||
className={classNames('scrollable', { fullscreen })}
|
||||
ref={this.setRef}
|
||||
>
|
||||
{ancestors}
|
||||
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={classNames('focusable', 'detailed-status__wrapper')} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
|
||||
<div
|
||||
className={classNames('focusable', 'detailed-status__wrapper')}
|
||||
tabIndex='0'
|
||||
aria-label={textForScreenReader(intl, status, false)}
|
||||
>
|
||||
<DetailedStatus
|
||||
status={status}
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
|
|
|
@ -48,69 +48,117 @@ class LinkFooter extends React.PureComponent {
|
|||
<div className='getting-started__footer'>
|
||||
<ul >
|
||||
<li >
|
||||
<a href="https://liberapay.com/cipherbliss">Supportez Cipherbliss</a>
|
||||
<a href='https://liberapay.com/cipherbliss'>Supportez Cipherbliss</a >
|
||||
</li >
|
||||
<li >
|
||||
<a href="/@tykayn">
|
||||
<i className="fa fa-envelope"></i>
|
||||
contactez Cipherbliss</a>
|
||||
<a href='https://mastodon.cipherbliss.com/@tykayn'>
|
||||
<i className='fa fa-paper-plane' />
|
||||
contactez nous</a >
|
||||
</li >
|
||||
<li >
|
||||
|
||||
<a href='/admin/tags?pending_review=1'>
|
||||
<i className="fa fa-fire"></i>
|
||||
<i className='fa fa-fire' />
|
||||
Trending hashtags</a >
|
||||
<hr />
|
||||
</li >
|
||||
{invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage
|
||||
{invitesEnabled && <li ><a
|
||||
href='/invites'
|
||||
target='_blank'
|
||||
><FormattedMessage
|
||||
id='getting_started.invite'
|
||||
defaultMessage='Invite people'
|
||||
/></a> ·
|
||||
/> ·</a >
|
||||
</li >}
|
||||
{withHotkeys && <li><Link to='/keyboard-shortcuts'><FormattedMessage
|
||||
{withHotkeys && <li ><Link to='/keyboard-shortcuts'>
|
||||
<FormattedMessage
|
||||
id='navigation_bar.keyboard_shortcuts'
|
||||
defaultMessage='Hotkeys'
|
||||
/></Link> ·
|
||||
/> ·
|
||||
</Link >
|
||||
</li >}
|
||||
<li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security'/></a> ·
|
||||
<li >
|
||||
<a href='/auth/edit'>
|
||||
<FormattedMessage
|
||||
id='getting_started.security'
|
||||
defaultMessage='Security'
|
||||
/> ·
|
||||
</a >
|
||||
</li >
|
||||
<li><a href='/about/more' target='_blank'><FormattedMessage
|
||||
<li >
|
||||
<a
|
||||
href='/about/more'
|
||||
target='_blank'
|
||||
><FormattedMessage
|
||||
id='navigation_bar.info'
|
||||
defaultMessage='About this server'
|
||||
/></a> ·
|
||||
/> ·
|
||||
</a >
|
||||
</li >
|
||||
<li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage
|
||||
<li >
|
||||
<a
|
||||
href='https://joinmastodon.org/apps'
|
||||
target='_blank'
|
||||
><FormattedMessage
|
||||
id='navigation_bar.apps'
|
||||
defaultMessage='Mobile apps'
|
||||
/></a> ·
|
||||
/> ·
|
||||
</a >
|
||||
</li >
|
||||
<li><a href='/terms' target='_blank'><FormattedMessage
|
||||
<li >
|
||||
<a
|
||||
href='/terms'
|
||||
target='_blank'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='getting_started.terms'
|
||||
defaultMessage='Terms of service'
|
||||
/></a> ·
|
||||
/> ·</a >
|
||||
</li >
|
||||
<li><a href='/settings/applications' target='_blank'><FormattedMessage
|
||||
<li >
|
||||
<a
|
||||
href='/settings/applications'
|
||||
target='_blank'
|
||||
><FormattedMessage
|
||||
id='getting_started.developers'
|
||||
defaultMessage='Developers'
|
||||
/></a> ·
|
||||
/> ·
|
||||
</a >
|
||||
</li >
|
||||
<li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage
|
||||
id='getting_started.documentation' defaultMessage='Documentation'
|
||||
/></a> ·
|
||||
<li >
|
||||
<a
|
||||
href='https://docs.joinmastodon.org'
|
||||
target='_blank'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='getting_started.documentation'
|
||||
defaultMessage='Documentation'
|
||||
/>
|
||||
</a > ·
|
||||
</li >
|
||||
<li><a href='/auth/sign_out' onClick={this.handleLogoutClick}><FormattedMessage
|
||||
<li >
|
||||
<a
|
||||
href='/auth/sign_out'
|
||||
onClick={this.handleLogoutClick}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='navigation_bar.logout'
|
||||
defaultMessage='Logout'
|
||||
/></a>
|
||||
/>
|
||||
</a >
|
||||
</li >
|
||||
</ul >
|
||||
|
||||
<p >
|
||||
<FormattedMessage
|
||||
id='getting_started.open_source_notice'
|
||||
defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}.'
|
||||
defaultMessage='Mastodon is open source software. You can contribute or report issues on the forge at {forge}.'
|
||||
values={{
|
||||
github: <span><a href={source_url} rel='noopener noreferrer' target='_blank'>{repository}</a> (v{version})</span>,
|
||||
forge: <span ><a
|
||||
href={source_url}
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
>{repository}</a > (v{version})</span >,
|
||||
}}
|
||||
/>
|
||||
</p >
|
||||
|
|
|
@ -14,14 +14,23 @@ const NavigationPanel = () => (
|
|||
<div className='navigation-panel'>
|
||||
|
||||
<NavLink
|
||||
className='column-link column-link--transparent' to='/timelines/home' data-preview-title-id='column.home'
|
||||
className='column-link column-link--transparent'
|
||||
to='/timelines/home'
|
||||
data-preview-title-id='column.home'
|
||||
data-preview-icon='home'
|
||||
><Icon className='column-link__icon' id='home' fixedWidth/><FormattedMessage
|
||||
id='tabs_bar.home' defaultMessage='Home'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='home'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.home'
|
||||
defaultMessage='Home'
|
||||
/></NavLink >
|
||||
<NavLink
|
||||
className='column-link column-link--transparent' to='/notifications'
|
||||
data-preview-title-id='column.notifications' data-preview-icon='bell'
|
||||
className='column-link column-link--transparent'
|
||||
to='/notifications'
|
||||
data-preview-title-id='column.notifications'
|
||||
data-preview-icon='bell'
|
||||
><NotificationsCounterIcon
|
||||
className='column-link__icon'
|
||||
/><FormattedMessage
|
||||
|
@ -30,79 +39,114 @@ const NavigationPanel = () => (
|
|||
/></NavLink >
|
||||
<FollowRequestsNavLink />
|
||||
<NavLink
|
||||
className='column-link column-link--transparent' to='/timelines/public/local'
|
||||
data-preview-title-id='column.community' data-preview-icon='users'
|
||||
className='column-link column-link--transparent'
|
||||
to='/timelines/public/local'
|
||||
data-preview-title-id='column.community'
|
||||
data-preview-icon='users'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='users'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.local_timeline' defaultMessage='Local'
|
||||
id='tabs_bar.local_timeline'
|
||||
defaultMessage='Local'
|
||||
/></NavLink >
|
||||
<NavLink
|
||||
className='column-link column-link--transparent' exact to='/timelines/public'
|
||||
data-preview-title-id='column.public' data-preview-icon='globe'
|
||||
className='column-link column-link--transparent'
|
||||
exact
|
||||
to='/timelines/public'
|
||||
data-preview-title-id='column.public'
|
||||
data-preview-icon='globe'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='globe'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.federated_timeline' defaultMessage='Federated'
|
||||
id='tabs_bar.federated_timeline'
|
||||
defaultMessage='Federated'
|
||||
/></NavLink >
|
||||
<NavLink className='column-link column-link--transparent' to='/timelines/direct'><Icon
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/timelines/direct'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='envelope'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.direct' defaultMessage='Direct messages'
|
||||
id='navigation_bar.direct'
|
||||
defaultMessage='Direct messages'
|
||||
/></NavLink >
|
||||
<NavLink className='column-link column-link--transparent' to='/favourites'><Icon
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/favourites'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='star'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.favourites' defaultMessage='Favourites'
|
||||
id='navigation_bar.favourites'
|
||||
defaultMessage='Favourites'
|
||||
/></NavLink >
|
||||
<NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/bookmarks'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='bookmark'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.bookmarks' defaultMessage='Bookmarks'
|
||||
id='navigation_bar.bookmarks'
|
||||
defaultMessage='Bookmarks'
|
||||
/></NavLink >
|
||||
<NavLink className='column-link column-link--transparent' to='/lists'><Icon
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/lists'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='list-ul'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.lists' defaultMessage='Lists'
|
||||
id='navigation_bar.lists'
|
||||
defaultMessage='Lists'
|
||||
/></NavLink >
|
||||
{profile_directory &&
|
||||
<NavLink className='column-link column-link--transparent' to='/directory'><Icon
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/directory'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='address-book-o'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='getting_started.directory' defaultMessage='Profile directory'
|
||||
id='getting_started.directory'
|
||||
defaultMessage='Profile directory'
|
||||
/></NavLink >}
|
||||
|
||||
<ListPanel />
|
||||
|
||||
<hr />
|
||||
|
||||
<a className='column-link column-link--transparent' href='/settings/preferences'><Icon
|
||||
<a
|
||||
className='column-link column-link--transparent'
|
||||
href='/settings/preferences'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='cog'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.preferences' defaultMessage='Preferences'
|
||||
id='navigation_bar.preferences'
|
||||
defaultMessage='Preferences'
|
||||
/></a >
|
||||
<a className='column-link column-link--transparent' href='/relationships'><Icon
|
||||
<a
|
||||
className='column-link column-link--transparent'
|
||||
href='/relationships'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='users'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers'
|
||||
id='navigation_bar.follows_and_followers'
|
||||
defaultMessage='Follows and followers'
|
||||
/></a >
|
||||
|
||||
{showTrends && <div className='flex-spacer' />}
|
||||
|
@ -110,7 +154,10 @@ const NavigationPanel = () => (
|
|||
|
||||
<div className='messaging-box'>
|
||||
<div className='title'>
|
||||
<i role='img' className='fa fa-envelope column-header__icon fa-fw'/>
|
||||
<i
|
||||
role='img'
|
||||
className='fa fa-envelope column-header__icon fa-fw'
|
||||
/>
|
||||
Messaging box
|
||||
</div >
|
||||
<div className='user-list column-header'>
|
||||
|
@ -134,23 +181,32 @@ const NavigationPanel = () => (
|
|||
<li className='conversations_item has-new-message'>
|
||||
<div className='title'>
|
||||
|
||||
<i role='img' className='fa fa-envelope column-header__icon fa-fw'/>
|
||||
<i
|
||||
role='img'
|
||||
className='fa fa-envelope column-header__icon fa-fw'
|
||||
/>
|
||||
Un Gens
|
||||
<span className='new-message-counter'>
|
||||
(3)</span >
|
||||
<button className='btn-small'>
|
||||
<i role='img' className='fa fa-caret-down column-header__icon fa-fw'/>
|
||||
<i
|
||||
role='img'
|
||||
className='fa fa-caret-down column-header__icon fa-fw'
|
||||
/>
|
||||
</button >
|
||||
</div >
|
||||
<div className='conversation_stream'>
|
||||
<div className='message theirs'>
|
||||
<p >oh hello there! 😋 </p >
|
||||
<div className='arrow-down' />
|
||||
</div >
|
||||
<div className='message mine'>
|
||||
<p >General Emoji</p >
|
||||
<div className='arrow-down' />
|
||||
</div >
|
||||
<div className='message theirs'>
|
||||
<p >we just achieved comedy</p >
|
||||
<div className='arrow-down' />
|
||||
</div >
|
||||
</div >
|
||||
<div className='conversation_input'>
|
||||
|
|
|
@ -853,11 +853,19 @@
|
|||
background: transparent;
|
||||
padding: 0;
|
||||
padding-top: 8px;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
img {
|
||||
transform: rotateY(180deg);
|
||||
|
||||
margin-left: 1ch;
|
||||
}
|
||||
}
|
||||
|
||||
.status__content__spoiler-link {
|
||||
|
@ -875,6 +883,13 @@
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.status__wrapper {
|
||||
&:hover {
|
||||
background: mix($ui-base-lighter-color, $ui-base-color);
|
||||
animation: background ease 0.5s;
|
||||
}
|
||||
}
|
||||
|
||||
.status__wrapper--filtered {
|
||||
color: $dark-text-color;
|
||||
border: 0;
|
||||
|
|
|
@ -137,7 +137,6 @@
|
|||
}
|
||||
|
||||
.getting-started__footer {
|
||||
text-align: justify;
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
|
|
|
@ -30,6 +30,10 @@ $messagingBoxHeight: 20em;
|
|||
}
|
||||
}
|
||||
|
||||
.conversation_created-at {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.conversation_stream {
|
||||
padding-top: 1em;
|
||||
height: $messagingBoxHeight;
|
||||
|
@ -42,16 +46,39 @@ $messagingBoxHeight: 20em;
|
|||
border-radius: 0.5rem;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0.5em 1em;
|
||||
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.mine {
|
||||
text-align: right;
|
||||
background: $classic-primary-color;
|
||||
float: right;
|
||||
|
||||
.arrow-down {
|
||||
border-top-color: $classic-primary-color;
|
||||
left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.theirs {
|
||||
text-align: left;
|
||||
background: $ui-highlight-color;
|
||||
float: left;
|
||||
|
||||
.arrow-down {
|
||||
border-top-color: $ui-highlight-color;
|
||||
right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow-down {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 20px solid transparent;
|
||||
border-top: 20px solid $classic-primary-color;
|
||||
position: relative;
|
||||
bottom: -1em;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,3 +20,11 @@
|
|||
- else
|
||||
= table_link_to 'circle', t('admin.accounts.web'), web_path("accounts/#{account.id}")
|
||||
= table_link_to 'globe', t('admin.accounts.public'), ActivityPub::TagManager.instance.url_for(account)
|
||||
%td
|
||||
= number_with_delimiter account.statuses_count
|
||||
%td
|
||||
= number_with_delimiter account.following_count
|
||||
%td
|
||||
= number_with_delimiter account.followers_count
|
||||
%td
|
||||
\-
|
||||
|
|
|
@ -44,10 +44,10 @@
|
|||
%th= t('admin.accounts.most_recent_ip')
|
||||
%th= t('admin.accounts.most_recent_activity')
|
||||
%th links
|
||||
%th sign in
|
||||
%th statuses
|
||||
%th following
|
||||
%th followers
|
||||
%th nuke
|
||||
%tbody
|
||||
= render @accounts
|
||||
|
||||
|
|
Loading…
Reference in New Issue