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
|
@ -12,7 +12,7 @@ import classNames from 'classnames';
|
||||||
const textAtCursorMatchesToken = (str, caretPosition) => {
|
const textAtCursorMatchesToken = (str, caretPosition) => {
|
||||||
let word;
|
let word;
|
||||||
|
|
||||||
let left = str.slice(0, caretPosition).search(/\S+$/);
|
let left = str.slice(0, caretPosition).search(/\S+$/);
|
||||||
let right = str.slice(caretPosition).search(/\s/);
|
let right = str.slice(caretPosition).search(/\s/);
|
||||||
|
|
||||||
if (right < 0) {
|
if (right < 0) {
|
||||||
|
@ -37,34 +37,37 @@ const textAtCursorMatchesToken = (str, caretPosition) => {
|
||||||
export default class AutosuggestTextarea extends ImmutablePureComponent {
|
export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
value: PropTypes.string,
|
value : PropTypes.string,
|
||||||
suggestions: ImmutablePropTypes.list,
|
suggestions : ImmutablePropTypes.list,
|
||||||
disabled: PropTypes.bool,
|
disabled : PropTypes.bool,
|
||||||
placeholder: PropTypes.string,
|
placeholder : PropTypes.string,
|
||||||
onSuggestionSelected: PropTypes.func.isRequired,
|
onSuggestionSelected : PropTypes.func.isRequired,
|
||||||
onSuggestionsClearRequested: PropTypes.func.isRequired,
|
onSuggestionsClearRequested: PropTypes.func.isRequired,
|
||||||
onSuggestionsFetchRequested: PropTypes.func.isRequired,
|
onSuggestionsFetchRequested: PropTypes.func.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange : PropTypes.func.isRequired,
|
||||||
onKeyUp: PropTypes.func,
|
onKeyUp : PropTypes.func,
|
||||||
onKeyDown: PropTypes.func,
|
onKeyDown : PropTypes.func,
|
||||||
onPaste: PropTypes.func.isRequired,
|
onPaste : PropTypes.func.isRequired,
|
||||||
autoFocus: PropTypes.bool,
|
autoFocus : PropTypes.bool,
|
||||||
|
directMessage : PropTypes.bool,
|
||||||
|
directMessageRecipient : PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
autoFocus: true,
|
autoFocus : true,
|
||||||
|
directMessage: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
suggestionsHidden: true,
|
suggestionsHidden : true,
|
||||||
focused: false,
|
focused : false,
|
||||||
selectedSuggestion: 0,
|
selectedSuggestion: 0,
|
||||||
lastToken: null,
|
lastToken : null,
|
||||||
tokenStart: 0,
|
tokenStart : 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
onChange = (e) => {
|
onChange = (e) => {
|
||||||
const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
|
const [tokenStart, token] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
|
||||||
|
|
||||||
if (token !== null && this.state.lastToken !== token) {
|
if (token !== null && this.state.lastToken !== token) {
|
||||||
this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart });
|
this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart });
|
||||||
|
@ -75,7 +78,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onChange(e);
|
this.props.onChange(e);
|
||||||
}
|
};
|
||||||
|
|
||||||
onKeyDown = (e) => {
|
onKeyDown = (e) => {
|
||||||
const { suggestions, disabled } = this.props;
|
const { suggestions, disabled } = this.props;
|
||||||
|
@ -92,7 +95,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(e.key) {
|
switch (e.key) {
|
||||||
case 'Escape':
|
case 'Escape':
|
||||||
if (suggestions.size === 0 || suggestionsHidden) {
|
if (suggestions.size === 0 || suggestionsHidden) {
|
||||||
document.querySelector('.ui').parentElement.focus();
|
document.querySelector('.ui').parentElement.focus();
|
||||||
|
@ -124,7 +127,6 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
|
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,27 +135,27 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onKeyDown(e);
|
this.props.onKeyDown(e);
|
||||||
}
|
};
|
||||||
|
|
||||||
onBlur = () => {
|
onBlur = () => {
|
||||||
this.setState({ suggestionsHidden: true, focused: false });
|
this.setState({ suggestionsHidden: true, focused: false });
|
||||||
}
|
};
|
||||||
|
|
||||||
onFocus = (e) => {
|
onFocus = (e) => {
|
||||||
this.setState({ focused: true });
|
this.setState({ focused: true });
|
||||||
if (this.props.onFocus) {
|
if (this.props.onFocus) {
|
||||||
this.props.onFocus(e);
|
this.props.onFocus(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onSuggestionClick = (e) => {
|
onSuggestionClick = (e) => {
|
||||||
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
|
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
|
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
|
||||||
this.textarea.focus();
|
this.textarea.focus();
|
||||||
}
|
};
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
|
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
|
||||||
this.setState({ suggestionsHidden: false });
|
this.setState({ suggestionsHidden: false });
|
||||||
}
|
}
|
||||||
|
@ -161,14 +163,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||||
|
|
||||||
setTextarea = (c) => {
|
setTextarea = (c) => {
|
||||||
this.textarea = c;
|
this.textarea = c;
|
||||||
}
|
};
|
||||||
|
|
||||||
onPaste = (e) => {
|
onPaste = (e) => {
|
||||||
if (e.clipboardData && e.clipboardData.files.length === 1) {
|
if (e.clipboardData && e.clipboardData.files.length === 1) {
|
||||||
this.props.onPaste(e.clipboardData.files);
|
this.props.onPaste(e.clipboardData.files);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
renderSuggestion = (suggestion, i) => {
|
renderSuggestion = (suggestion, i) => {
|
||||||
const { selectedSuggestion } = this.state;
|
const { selectedSuggestion } = this.state;
|
||||||
|
@ -176,23 +178,30 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||||
|
|
||||||
if (suggestion.type === 'emoji') {
|
if (suggestion.type === 'emoji') {
|
||||||
inner = <AutosuggestEmoji emoji={suggestion} />;
|
inner = <AutosuggestEmoji emoji={suggestion} />;
|
||||||
key = suggestion.id;
|
key = suggestion.id;
|
||||||
} else if (suggestion.type === 'hashtag') {
|
} else if (suggestion.type === 'hashtag') {
|
||||||
inner = <AutosuggestHashtag tag={suggestion} />;
|
inner = <AutosuggestHashtag tag={suggestion} />;
|
||||||
key = suggestion.name;
|
key = suggestion.name;
|
||||||
} else if (suggestion.type === 'account') {
|
} else if (suggestion.type === 'account') {
|
||||||
inner = <AutosuggestAccountContainer id={suggestion.id} />;
|
inner = <AutosuggestAccountContainer id={suggestion.id} />;
|
||||||
key = suggestion.id;
|
key = suggestion.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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}
|
{inner}
|
||||||
</div>
|
</div >
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props;
|
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props;
|
||||||
const { suggestionsHidden } = this.state;
|
const { suggestionsHidden } = this.state;
|
||||||
const style = { direction: 'ltr' };
|
const style = { direction: 'ltr' };
|
||||||
|
@ -202,10 +211,13 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<div className='compose-form__autosuggest-wrapper' key='autosuggest-wrapper'>
|
<div
|
||||||
|
className='compose-form__autosuggest-wrapper'
|
||||||
|
key='autosuggest-wrapper'
|
||||||
|
>
|
||||||
<div className='autosuggest-textarea'>
|
<div className='autosuggest-textarea'>
|
||||||
<label>
|
<label >
|
||||||
<span style={{ display: 'none' }}>{placeholder}</span>
|
<span style={{ display: 'none' }}>{placeholder}</span >
|
||||||
|
|
||||||
<Textarea
|
<Textarea
|
||||||
inputRef={this.setTextarea}
|
inputRef={this.setTextarea}
|
||||||
|
@ -223,16 +235,21 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||||
style={style}
|
style={style}
|
||||||
aria-autocomplete='list'
|
aria-autocomplete='list'
|
||||||
/>
|
/>
|
||||||
</label>
|
</label >
|
||||||
</div>
|
</div >
|
||||||
{children}
|
{children}
|
||||||
</div>,
|
</div >,
|
||||||
|
|
||||||
<div className='autosuggest-textarea__suggestions-wrapper' key='suggestions-wrapper'>
|
<div
|
||||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
|
className='autosuggest-textarea__suggestions-wrapper'
|
||||||
|
key='suggestions-wrapper'
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}
|
||||||
|
>
|
||||||
{suggestions.map(this.renderSuggestion)}
|
{suggestions.map(this.renderSuggestion)}
|
||||||
</div>
|
</div >
|
||||||
</div>,
|
</div >,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,13 @@ import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import Icon from 'mastodon/components/icon';
|
import Icon from 'mastodon/components/icon';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
|
show : { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
|
||||||
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
|
hide : { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
|
||||||
moveLeft: { id: 'column_header.moveLeft_settings', defaultMessage: 'Move column to the left' },
|
moveLeft : { id: 'column_header.moveLeft_settings', defaultMessage: 'Move column to the left' },
|
||||||
moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
|
moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -20,19 +20,19 @@ class ColumnHeader extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
intl: PropTypes.object.isRequired,
|
intl : PropTypes.object.isRequired,
|
||||||
title: PropTypes.node,
|
title : PropTypes.node,
|
||||||
icon: PropTypes.string,
|
icon : PropTypes.string,
|
||||||
active: PropTypes.bool,
|
active : PropTypes.bool,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn : PropTypes.bool,
|
||||||
extraButton: PropTypes.node,
|
extraButton : PropTypes.node,
|
||||||
showBackButton: PropTypes.bool,
|
showBackButton: PropTypes.bool,
|
||||||
children: PropTypes.node,
|
children : PropTypes.node,
|
||||||
pinned: PropTypes.bool,
|
pinned : PropTypes.bool,
|
||||||
placeholder: PropTypes.bool,
|
placeholder : PropTypes.bool,
|
||||||
onPin: PropTypes.func,
|
onPin : PropTypes.func,
|
||||||
onMove: PropTypes.func,
|
onMove : PropTypes.func,
|
||||||
onClick: PropTypes.func,
|
onClick : PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -46,41 +46,41 @@ class ColumnHeader extends React.PureComponent {
|
||||||
} else {
|
} else {
|
||||||
this.context.router.history.goBack();
|
this.context.router.history.goBack();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleToggleClick = (e) => {
|
handleToggleClick = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.setState({ collapsed: !this.state.collapsed, animating: true });
|
this.setState({ collapsed: !this.state.collapsed, animating: true });
|
||||||
}
|
};
|
||||||
|
|
||||||
handleTitleClick = () => {
|
handleTitleClick = () => {
|
||||||
this.props.onClick();
|
this.props.onClick();
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMoveLeft = () => {
|
handleMoveLeft = () => {
|
||||||
this.props.onMove(-1);
|
this.props.onMove(-1);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMoveRight = () => {
|
handleMoveRight = () => {
|
||||||
this.props.onMove(1);
|
this.props.onMove(1);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleBackClick = () => {
|
handleBackClick = () => {
|
||||||
this.historyBack();
|
this.historyBack();
|
||||||
}
|
};
|
||||||
|
|
||||||
handleTransitionEnd = () => {
|
handleTransitionEnd = () => {
|
||||||
this.setState({ animating: false });
|
this.setState({ animating: false });
|
||||||
}
|
};
|
||||||
|
|
||||||
handlePin = () => {
|
handlePin = () => {
|
||||||
if (!this.props.pinned) {
|
if (!this.props.pinned) {
|
||||||
this.historyBack();
|
this.historyBack();
|
||||||
}
|
}
|
||||||
this.props.onPin();
|
this.props.onPin();
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder } = this.props;
|
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder } = this.props;
|
||||||
const { collapsed, animating } = this.state;
|
const { collapsed, animating } = this.state;
|
||||||
|
|
||||||
|
@ -105,31 +105,82 @@ class ColumnHeader extends React.PureComponent {
|
||||||
|
|
||||||
if (children) {
|
if (children) {
|
||||||
extraContent = (
|
extraContent = (
|
||||||
<div key='extra-content' className='column-header__collapsible__extra'>
|
<div
|
||||||
|
key='extra-content'
|
||||||
|
className='column-header__collapsible__extra'
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multiColumn && pinned) {
|
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 = (
|
moveButtons = (
|
||||||
<div key='move-buttons' className='column-header__setting-arrows'>
|
<div
|
||||||
<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>
|
key='move-buttons'
|
||||||
<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>
|
className='column-header__setting-arrows'
|
||||||
</div>
|
>
|
||||||
|
<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) {
|
} 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)) {
|
if (!pinned && (multiColumn || showBackButton)) {
|
||||||
backButton = (
|
backButton = (
|
||||||
<button onClick={this.handleBackClick} className='column-header__back-button'>
|
<button
|
||||||
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
|
onClick={this.handleBackClick}
|
||||||
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
className='column-header__back-button'
|
||||||
</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)) {
|
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;
|
const hasTitle = icon && title;
|
||||||
|
@ -153,9 +213,13 @@ class ColumnHeader extends React.PureComponent {
|
||||||
<h1 className={buttonClassName}>
|
<h1 className={buttonClassName}>
|
||||||
{hasTitle && (
|
{hasTitle && (
|
||||||
<button onClick={this.handleTitleClick}>
|
<button onClick={this.handleTitleClick}>
|
||||||
<Icon id={icon} fixedWidth className='column-header__icon' />
|
<Icon
|
||||||
|
id={icon}
|
||||||
|
fixedWidth
|
||||||
|
className='column-header__icon'
|
||||||
|
/>
|
||||||
{title}
|
{title}
|
||||||
</button>
|
</button >
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!hasTitle && backButton}
|
{!hasTitle && backButton}
|
||||||
|
@ -164,15 +228,19 @@ class ColumnHeader extends React.PureComponent {
|
||||||
{hasTitle && backButton}
|
{hasTitle && backButton}
|
||||||
{extraButton}
|
{extraButton}
|
||||||
{collapseButton}
|
{collapseButton}
|
||||||
</div>
|
</div >
|
||||||
</h1>
|
</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'>
|
<div className='column-header__collapsible-inner'>
|
||||||
{(!collapsed || animating) && collapsedContent}
|
{(!collapsed || animating) && collapsedContent}
|
||||||
</div>
|
</div >
|
||||||
</div>
|
</div >
|
||||||
</div>
|
</div >
|
||||||
);
|
);
|
||||||
|
|
||||||
if (multiColumn || placeholder) {
|
if (multiColumn || placeholder) {
|
||||||
|
|
|
@ -6,12 +6,12 @@ import { autoPlayGif } from 'mastodon/initial_state';
|
||||||
export default class DisplayName extends React.PureComponent {
|
export default class DisplayName extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
account : ImmutablePropTypes.map.isRequired,
|
||||||
others: ImmutablePropTypes.list,
|
others : ImmutablePropTypes.list,
|
||||||
localDomain: PropTypes.string,
|
localDomain: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
_updateEmojis () {
|
_updateEmojis() {
|
||||||
const node = this.node;
|
const node = this.node;
|
||||||
|
|
||||||
if (!node || autoPlayGif) {
|
if (!node || autoPlayGif) {
|
||||||
|
@ -32,33 +32,40 @@ export default class DisplayName extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
this._updateEmojis();
|
this._updateEmojis();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate() {
|
||||||
this._updateEmojis();
|
this._updateEmojis();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEmojiMouseEnter = ({ target }) => {
|
handleEmojiMouseEnter = ({ target }) => {
|
||||||
target.src = target.getAttribute('data-original');
|
target.src = target.getAttribute('data-original');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleEmojiMouseLeave = ({ target }) => {
|
handleEmojiMouseLeave = ({ target }) => {
|
||||||
target.src = target.getAttribute('data-static');
|
target.src = target.getAttribute('data-static');
|
||||||
}
|
};
|
||||||
|
|
||||||
setRef = (c) => {
|
setRef = (c) => {
|
||||||
this.node = c;
|
this.node = c;
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { others, localDomain } = this.props;
|
const { others, localDomain } = this.props;
|
||||||
|
|
||||||
let displayName, suffix, account;
|
let displayName, suffix, account;
|
||||||
|
|
||||||
if (others && others.size > 1) {
|
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) {
|
if (others.size - 2 > 0) {
|
||||||
suffix = `+${others.size - 2}`;
|
suffix = `+${others.size - 2}`;
|
||||||
|
@ -76,14 +83,22 @@ export default class DisplayName extends React.PureComponent {
|
||||||
acct = `${acct}@${localDomain}`;
|
acct = `${acct}@${localDomain}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
displayName = <bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi>;
|
displayName = <bdi ><strong
|
||||||
suffix = <span className='display-name__account'>@{acct}</span>;
|
className='display-name__html'
|
||||||
|
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</bdi >;
|
||||||
|
suffix = <span className='display-name__account'>@{acct}</span >;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className='display-name' ref={this.setRef}>
|
<span
|
||||||
|
className='display-name'
|
||||||
|
ref={this.setRef}
|
||||||
|
>
|
||||||
{displayName} {suffix}
|
{displayName} {suffix}
|
||||||
</span>
|
</span >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,17 +10,17 @@ import StatusContent from './status_content';
|
||||||
import StatusActionBar from './status_action_bar';
|
import StatusActionBar from './status_action_bar';
|
||||||
import AttachmentList from './attachment_list';
|
import AttachmentList from './attachment_list';
|
||||||
import Card from '../features/status/components/card';
|
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 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 { HotKeys } from 'react-hotkeys';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Icon from 'mastodon/components/icon';
|
import Icon from 'mastodon/components/icon';
|
||||||
import { displayMedia } from '../initial_state';
|
import { displayMedia } from '../initial_state';
|
||||||
|
|
||||||
// We use the component (and not the container) since we do not want
|
// We use the component (and not the container) since we do not want
|
||||||
// to use the progress bar to show download progress
|
// to use the progress bar to show download progress
|
||||||
import Bundle from '../features/ui/components/bundle';
|
import Bundle from '../features/ui/components/bundle';
|
||||||
|
import imageShowThread from '../../images/icon_reply.svg';
|
||||||
|
|
||||||
export const textForScreenReader = (intl, status, rebloggedByText = false) => {
|
export const textForScreenReader = (intl, status, rebloggedByText = false) => {
|
||||||
const displayName = status.getIn(['account', 'display_name']);
|
const displayName = status.getIn(['account', 'display_name']);
|
||||||
|
@ -59,33 +59,33 @@ class Status extends ImmutablePureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
status: ImmutablePropTypes.map,
|
status : ImmutablePropTypes.map,
|
||||||
account: ImmutablePropTypes.map,
|
account : ImmutablePropTypes.map,
|
||||||
otherAccounts: ImmutablePropTypes.list,
|
otherAccounts : ImmutablePropTypes.list,
|
||||||
onClick: PropTypes.func,
|
onClick : PropTypes.func,
|
||||||
onReply: PropTypes.func,
|
onReply : PropTypes.func,
|
||||||
onFavourite: PropTypes.func,
|
onFavourite : PropTypes.func,
|
||||||
onReblog: PropTypes.func,
|
onReblog : PropTypes.func,
|
||||||
onDelete: PropTypes.func,
|
onDelete : PropTypes.func,
|
||||||
onDirect: PropTypes.func,
|
onDirect : PropTypes.func,
|
||||||
onMention: PropTypes.func,
|
onMention : PropTypes.func,
|
||||||
onPin: PropTypes.func,
|
onPin : PropTypes.func,
|
||||||
onOpenMedia: PropTypes.func,
|
onOpenMedia : PropTypes.func,
|
||||||
onOpenVideo: PropTypes.func,
|
onOpenVideo : PropTypes.func,
|
||||||
onBlock: PropTypes.func,
|
onBlock : PropTypes.func,
|
||||||
onEmbed: PropTypes.func,
|
onEmbed : PropTypes.func,
|
||||||
onHeightChange: PropTypes.func,
|
onHeightChange : PropTypes.func,
|
||||||
onToggleHidden: PropTypes.func,
|
onToggleHidden : PropTypes.func,
|
||||||
muted: PropTypes.bool,
|
muted : PropTypes.bool,
|
||||||
hidden: PropTypes.bool,
|
hidden : PropTypes.bool,
|
||||||
unread: PropTypes.bool,
|
unread : PropTypes.bool,
|
||||||
onMoveUp: PropTypes.func,
|
onMoveUp : PropTypes.func,
|
||||||
onMoveDown: PropTypes.func,
|
onMoveDown : PropTypes.func,
|
||||||
showThread: PropTypes.bool,
|
showThread : PropTypes.bool,
|
||||||
getScrollPosition: PropTypes.func,
|
getScrollPosition : PropTypes.func,
|
||||||
updateScrollBottom: PropTypes.func,
|
updateScrollBottom: PropTypes.func,
|
||||||
cacheMediaWidth: PropTypes.func,
|
cacheMediaWidth : PropTypes.func,
|
||||||
cachedMediaWidth: PropTypes.number,
|
cachedMediaWidth : PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Avoid checking props that are functions (and whose equality will always
|
// Avoid checking props that are functions (and whose equality will always
|
||||||
|
@ -99,15 +99,26 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
showMedia: defaultMediaVisibility(this.props.status),
|
showMedia: defaultMediaVisibility(this.props.status),
|
||||||
statusId: undefined,
|
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
|
// Track height changes we know about to compensate scrolling
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
|
this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
|
||||||
}
|
}
|
||||||
|
|
||||||
getSnapshotBeforeUpdate () {
|
getSnapshotBeforeUpdate() {
|
||||||
if (this.props.getScrollPosition) {
|
if (this.props.getScrollPosition) {
|
||||||
return this.props.getScrollPosition();
|
return this.props.getScrollPosition();
|
||||||
} else {
|
} else {
|
||||||
|
@ -115,20 +126,9 @@ 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
|
// Compensate height changes
|
||||||
componentDidUpdate (prevProps, prevState, snapshot) {
|
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||||
const doShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
|
const doShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
|
||||||
|
|
||||||
if (doShowCard && !this.didShowCard) {
|
if (doShowCard && !this.didShowCard) {
|
||||||
this.didShowCard = true;
|
this.didShowCard = true;
|
||||||
|
@ -154,7 +154,7 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
handleToggleMediaVisibility = () => {
|
handleToggleMediaVisibility = () => {
|
||||||
this.setState({ showMedia: !this.state.showMedia });
|
this.setState({ showMedia: !this.state.showMedia });
|
||||||
}
|
};
|
||||||
|
|
||||||
handleClick = () => {
|
handleClick = () => {
|
||||||
if (this.props.onClick) {
|
if (this.props.onClick) {
|
||||||
|
@ -168,7 +168,7 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
const { status } = this.props;
|
const { status } = this.props;
|
||||||
this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
|
this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleExpandClick = (e) => {
|
handleExpandClick = (e) => {
|
||||||
if (this.props.onClick) {
|
if (this.props.onClick) {
|
||||||
|
@ -184,7 +184,7 @@ class Status extends ImmutablePureComponent {
|
||||||
const { status } = this.props;
|
const { status } = this.props;
|
||||||
this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
|
this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleAccountClick = (e) => {
|
handleAccountClick = (e) => {
|
||||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||||
|
@ -192,27 +192,36 @@ class Status extends ImmutablePureComponent {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.context.router.history.push(`/accounts/${id}`);
|
this.context.router.history.push(`/accounts/${id}`);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleExpandedToggle = () => {
|
handleExpandedToggle = () => {
|
||||||
this.props.onToggleHidden(this._properStatus());
|
this.props.onToggleHidden(this._properStatus());
|
||||||
};
|
};
|
||||||
|
|
||||||
renderLoadingMediaGallery () {
|
renderLoadingMediaGallery() {
|
||||||
return <div className='media-gallery' style={{ height: '110px' }} />;
|
return <div
|
||||||
|
className='media-gallery'
|
||||||
|
style={{ height: '110px' }}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoadingVideoPlayer () {
|
renderLoadingVideoPlayer() {
|
||||||
return <div className='video-player' style={{ height: '110px' }} />;
|
return <div
|
||||||
|
className='video-player'
|
||||||
|
style={{ height: '110px' }}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoadingAudioPlayer () {
|
renderLoadingAudioPlayer() {
|
||||||
return <div className='audio-player' style={{ height: '110px' }} />;
|
return <div
|
||||||
|
className='audio-player'
|
||||||
|
style={{ height: '110px' }}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOpenVideo = (media, startTime) => {
|
handleOpenVideo = (media, startTime) => {
|
||||||
this.props.onOpenVideo(media, startTime);
|
this.props.onOpenVideo(media, startTime);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyOpenMedia = e => {
|
handleHotkeyOpenMedia = e => {
|
||||||
const { onOpenMedia, onOpenVideo } = this.props;
|
const { onOpenMedia, onOpenVideo } = this.props;
|
||||||
|
@ -229,51 +238,51 @@ class Status extends ImmutablePureComponent {
|
||||||
onOpenMedia(status.get('media_attachments'), 0);
|
onOpenMedia(status.get('media_attachments'), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyReply = e => {
|
handleHotkeyReply = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onReply(this._properStatus(), this.context.router.history);
|
this.props.onReply(this._properStatus(), this.context.router.history);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyFavourite = () => {
|
handleHotkeyFavourite = () => {
|
||||||
this.props.onFavourite(this._properStatus());
|
this.props.onFavourite(this._properStatus());
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyBoost = e => {
|
handleHotkeyBoost = e => {
|
||||||
this.props.onReblog(this._properStatus(), e);
|
this.props.onReblog(this._properStatus(), e);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyMention = e => {
|
handleHotkeyMention = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onMention(this._properStatus().get('account'), this.context.router.history);
|
this.props.onMention(this._properStatus().get('account'), this.context.router.history);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyOpen = () => {
|
handleHotkeyOpen = () => {
|
||||||
this.context.router.history.push(`/statuses/${this._properStatus().get('id')}`);
|
this.context.router.history.push(`/statuses/${this._properStatus().get('id')}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyOpenProfile = () => {
|
handleHotkeyOpenProfile = () => {
|
||||||
this.context.router.history.push(`/accounts/${this._properStatus().getIn(['account', 'id'])}`);
|
this.context.router.history.push(`/accounts/${this._properStatus().getIn(['account', 'id'])}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyMoveUp = e => {
|
handleHotkeyMoveUp = e => {
|
||||||
this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured'));
|
this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured'));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyMoveDown = e => {
|
handleHotkeyMoveDown = e => {
|
||||||
this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured'));
|
this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured'));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyToggleHidden = () => {
|
handleHotkeyToggleHidden = () => {
|
||||||
this.props.onToggleHidden(this._properStatus());
|
this.props.onToggleHidden(this._properStatus());
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyToggleSensitive = () => {
|
handleHotkeyToggleSensitive = () => {
|
||||||
this.handleToggleMediaVisibility();
|
this.handleToggleMediaVisibility();
|
||||||
}
|
};
|
||||||
|
|
||||||
_properStatus () {
|
_properStatus() {
|
||||||
const { status } = this.props;
|
const { status } = this.props;
|
||||||
|
|
||||||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||||
|
@ -285,9 +294,9 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
handleRef = c => {
|
handleRef = c => {
|
||||||
this.node = c;
|
this.node = c;
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
let media = null;
|
let media = null;
|
||||||
let statusAvatar, prepend, rebloggedByText;
|
let statusAvatar, prepend, rebloggedByText;
|
||||||
|
|
||||||
|
@ -300,66 +309,105 @@ class Status extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlers = this.props.muted ? {} : {
|
const handlers = this.props.muted ? {} : {
|
||||||
reply: this.handleHotkeyReply,
|
reply : this.handleHotkeyReply,
|
||||||
favourite: this.handleHotkeyFavourite,
|
favourite : this.handleHotkeyFavourite,
|
||||||
boost: this.handleHotkeyBoost,
|
boost : this.handleHotkeyBoost,
|
||||||
mention: this.handleHotkeyMention,
|
mention : this.handleHotkeyMention,
|
||||||
open: this.handleHotkeyOpen,
|
open : this.handleHotkeyOpen,
|
||||||
openProfile: this.handleHotkeyOpenProfile,
|
openProfile : this.handleHotkeyOpenProfile,
|
||||||
moveUp: this.handleHotkeyMoveUp,
|
moveUp : this.handleHotkeyMoveUp,
|
||||||
moveDown: this.handleHotkeyMoveDown,
|
moveDown : this.handleHotkeyMoveDown,
|
||||||
toggleHidden: this.handleHotkeyToggleHidden,
|
toggleHidden : this.handleHotkeyToggleHidden,
|
||||||
toggleSensitive: this.handleHotkeyToggleSensitive,
|
toggleSensitive: this.handleHotkeyToggleSensitive,
|
||||||
openMedia: this.handleHotkeyOpenMedia,
|
openMedia : this.handleHotkeyOpenMedia,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={handlers}>
|
<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.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}
|
||||||
{status.get('content')}
|
{status.get('content')}
|
||||||
</div>
|
</div >
|
||||||
</HotKeys>
|
</HotKeys >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.get('filtered') || status.getIn(['reblog', 'filtered'])) {
|
if (status.get('filtered') || status.getIn(['reblog', 'filtered'])) {
|
||||||
const minHandlers = this.props.muted ? {} : {
|
const minHandlers = this.props.muted ? {} : {
|
||||||
moveUp: this.handleHotkeyMoveUp,
|
moveUp : this.handleHotkeyMoveUp,
|
||||||
moveDown: this.handleHotkeyMoveDown,
|
moveDown: this.handleHotkeyMoveDown,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={minHandlers}>
|
<HotKeys handlers={minHandlers}>
|
||||||
<div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0' ref={this.handleRef}>
|
<div
|
||||||
<FormattedMessage id='status.filtered' defaultMessage='Filtered' />
|
className='status__wrapper status__wrapper--filtered focusable'
|
||||||
</div>
|
tabIndex='0'
|
||||||
</HotKeys>
|
ref={this.handleRef}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='status.filtered'
|
||||||
|
defaultMessage='Filtered'
|
||||||
|
/>
|
||||||
|
</div >
|
||||||
|
</HotKeys >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (featured) {
|
if (featured) {
|
||||||
prepend = (
|
prepend = (
|
||||||
<div className='status__prepend'>
|
<div className='status__prepend'>
|
||||||
<div className='status__prepend-icon-wrapper'><Icon id='thumb-tack' className='status__prepend-icon' fixedWidth /></div>
|
<div className='status__prepend-icon-wrapper'><Icon
|
||||||
<FormattedMessage id='status.pinned' defaultMessage='Pinned toot' />
|
id='thumb-tack'
|
||||||
</div>
|
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') {
|
} else if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||||
const display_name_html = { __html: status.getIn(['account', 'display_name_html']) };
|
const display_name_html = { __html: status.getIn(['account', 'display_name_html']) };
|
||||||
|
|
||||||
prepend = (
|
prepend = (
|
||||||
<div className='status__prepend'>
|
<div className='status__prepend'>
|
||||||
<div className='status__prepend-icon-wrapper'><Icon id='retweet' className='status__prepend-icon' fixedWidth /></div>
|
<div className='status__prepend-icon-wrapper'><Icon
|
||||||
<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> }} />
|
id='retweet'
|
||||||
</div>
|
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');
|
account = status.get('account');
|
||||||
status = status.get('reblog');
|
status = status.get('reblog');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.get('media_attachments').size > 0) {
|
if (status.get('media_attachments').size > 0) {
|
||||||
|
@ -374,7 +422,10 @@ class Status extends ImmutablePureComponent {
|
||||||
const attachment = status.getIn(['media_attachments', 0]);
|
const attachment = status.getIn(['media_attachments', 0]);
|
||||||
|
|
||||||
media = (
|
media = (
|
||||||
<Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} >
|
<Bundle
|
||||||
|
fetchComponent={Audio}
|
||||||
|
loading={this.renderLoadingAudioPlayer}
|
||||||
|
>
|
||||||
{Component => (
|
{Component => (
|
||||||
<Component
|
<Component
|
||||||
src={attachment.get('url')}
|
src={attachment.get('url')}
|
||||||
|
@ -384,13 +435,16 @@ class Status extends ImmutablePureComponent {
|
||||||
height={70}
|
height={70}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Bundle>
|
</Bundle >
|
||||||
);
|
);
|
||||||
} else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
|
} else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
|
||||||
const attachment = status.getIn(['media_attachments', 0]);
|
const attachment = status.getIn(['media_attachments', 0]);
|
||||||
|
|
||||||
media = (
|
media = (
|
||||||
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
|
<Bundle
|
||||||
|
fetchComponent={Video}
|
||||||
|
loading={this.renderLoadingVideoPlayer}
|
||||||
|
>
|
||||||
{Component => (
|
{Component => (
|
||||||
<Component
|
<Component
|
||||||
preview={attachment.get('preview_url')}
|
preview={attachment.get('preview_url')}
|
||||||
|
@ -407,11 +461,14 @@ class Status extends ImmutablePureComponent {
|
||||||
onToggleVisibility={this.handleToggleMediaVisibility}
|
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Bundle>
|
</Bundle >
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
media = (
|
media = (
|
||||||
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
|
<Bundle
|
||||||
|
fetchComponent={MediaGallery}
|
||||||
|
loading={this.renderLoadingMediaGallery}
|
||||||
|
>
|
||||||
{Component => (
|
{Component => (
|
||||||
<Component
|
<Component
|
||||||
media={status.get('media_attachments')}
|
media={status.get('media_attachments')}
|
||||||
|
@ -424,7 +481,7 @@ class Status extends ImmutablePureComponent {
|
||||||
onToggleVisibility={this.handleToggleMediaVisibility}
|
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Bundle>
|
</Bundle >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (status.get('spoiler_text').length === 0 && status.get('card')) {
|
} else if (status.get('spoiler_text').length === 0 && status.get('card')) {
|
||||||
|
@ -440,46 +497,111 @@ class Status extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otherAccounts && otherAccounts.size > 0) {
|
if (otherAccounts && otherAccounts.size > 0) {
|
||||||
statusAvatar = <AvatarComposite accounts={otherAccounts} size={48} />;
|
statusAvatar = <AvatarComposite
|
||||||
|
accounts={otherAccounts}
|
||||||
|
size={48}
|
||||||
|
/>;
|
||||||
} else if (account === undefined || account === null) {
|
} else if (account === undefined || account === null) {
|
||||||
statusAvatar = <Avatar account={status.get('account')} size={48} />;
|
statusAvatar = <Avatar
|
||||||
|
account={status.get('account')}
|
||||||
|
size={48}
|
||||||
|
/>;
|
||||||
} else {
|
} else {
|
||||||
statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />;
|
statusAvatar = <AvatarOverlay
|
||||||
|
account={status.get('account')}
|
||||||
|
friend={account}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={handlers}>
|
<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}
|
{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
|
||||||
<div className='status__expand' onClick={this.handleExpandClick} role='presentation' />
|
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'>
|
<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'>
|
<div className='status__avatar'>
|
||||||
{statusAvatar}
|
{statusAvatar}
|
||||||
</div>
|
</div >
|
||||||
|
|
||||||
<DisplayName account={status.get('account')} others={otherAccounts} />
|
<DisplayName
|
||||||
</a>
|
account={status.get('account')}
|
||||||
</div>
|
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}
|
{media}
|
||||||
|
|
||||||
{showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) && (
|
{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}>
|
<button
|
||||||
<FormattedMessage id='status.show_thread' defaultMessage='Show thread' />
|
className='status__content__read-more-button'
|
||||||
</button>
|
onClick={this.handleClick}
|
||||||
|
>
|
||||||
|
|
||||||
|
<FormattedMessage
|
||||||
|
id='status.show_thread'
|
||||||
|
defaultMessage='Show thread'
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src={imageShowThread}
|
||||||
|
alt='=> '
|
||||||
|
/>
|
||||||
|
</button >
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<StatusActionBar status={status} account={account} {...other} />
|
<StatusActionBar
|
||||||
</div>
|
status={status}
|
||||||
</div>
|
account={account} {...other} />
|
||||||
</HotKeys>
|
</div >
|
||||||
|
</div >
|
||||||
|
</HotKeys >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,36 +5,36 @@ import IconButton from '../../../components/icon_button';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
|
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import { me, isStaff } from '../../../initial_state';
|
import { isStaff, me } from '../../../initial_state';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
delete : { id: 'status.delete', defaultMessage: 'Delete' },
|
||||||
redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
|
redraft : { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
|
||||||
direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
|
direct : { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
|
||||||
mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
|
mention : { id: 'status.mention', defaultMessage: 'Mention @{name}' },
|
||||||
reply: { id: 'status.reply', defaultMessage: 'Reply' },
|
reply : { id: 'status.reply', defaultMessage: 'Reply' },
|
||||||
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
|
reblog : { id: 'status.reblog', defaultMessage: 'Boost' },
|
||||||
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
|
reblog_private : { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
|
||||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
cannot_reblog : { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
favourite : { id: 'status.favourite', defaultMessage: 'Favourite' },
|
||||||
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
bookmark : { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
||||||
mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' },
|
mute : { id: 'status.mute', defaultMessage: 'Mute @{name}' },
|
||||||
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
muteConversation : { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
||||||
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
|
unmuteConversation : { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
|
||||||
block: { id: 'status.block', defaultMessage: 'Block @{name}' },
|
block : { id: 'status.block', defaultMessage: 'Block @{name}' },
|
||||||
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
|
report : { id: 'status.report', defaultMessage: 'Report @{name}' },
|
||||||
share: { id: 'status.share', defaultMessage: 'Share' },
|
share : { id: 'status.share', defaultMessage: 'Share' },
|
||||||
pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
|
pin : { id: 'status.pin', defaultMessage: 'Pin on profile' },
|
||||||
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
unpin : { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
||||||
embed: { id: 'status.embed', defaultMessage: 'Embed' },
|
embed : { id: 'status.embed', defaultMessage: 'Embed' },
|
||||||
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
admin_account : { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||||
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
|
admin_status : { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
|
||||||
copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
|
copy : { id: 'status.copy', defaultMessage: 'Copy link to status' },
|
||||||
blockDomain: { id: 'account.block_domain', defaultMessage: 'Hide everything from {domain}' },
|
blockDomain : { id: 'account.block_domain', defaultMessage: 'Hide everything from {domain}' },
|
||||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
|
unblockDomain : { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
|
||||||
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
unmute : { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
||||||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
unblock : { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = (state, { status }) => ({
|
const mapStateToProps = (state, { status }) => ({
|
||||||
|
@ -50,59 +50,59 @@ class ActionBar extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
status: ImmutablePropTypes.map.isRequired,
|
status : ImmutablePropTypes.map.isRequired,
|
||||||
relationship: ImmutablePropTypes.map,
|
relationship : ImmutablePropTypes.map,
|
||||||
onReply: PropTypes.func.isRequired,
|
onReply : PropTypes.func.isRequired,
|
||||||
onReblog: PropTypes.func.isRequired,
|
onReblog : PropTypes.func.isRequired,
|
||||||
onFavourite: PropTypes.func.isRequired,
|
onFavourite : PropTypes.func.isRequired,
|
||||||
onBookmark: PropTypes.func.isRequired,
|
onBookmark : PropTypes.func.isRequired,
|
||||||
onDelete: PropTypes.func.isRequired,
|
onDelete : PropTypes.func.isRequired,
|
||||||
onDirect: PropTypes.func.isRequired,
|
onDirect : PropTypes.func.isRequired,
|
||||||
onMention: PropTypes.func.isRequired,
|
onMention : PropTypes.func.isRequired,
|
||||||
onMute: PropTypes.func,
|
onMute : PropTypes.func,
|
||||||
onUnmute: PropTypes.func,
|
onUnmute : PropTypes.func,
|
||||||
onBlock: PropTypes.func,
|
onBlock : PropTypes.func,
|
||||||
onUnblock: PropTypes.func,
|
onUnblock : PropTypes.func,
|
||||||
onBlockDomain: PropTypes.func,
|
onBlockDomain : PropTypes.func,
|
||||||
onUnblockDomain: PropTypes.func,
|
onUnblockDomain : PropTypes.func,
|
||||||
onMuteConversation: PropTypes.func,
|
onMuteConversation: PropTypes.func,
|
||||||
onReport: PropTypes.func,
|
onReport : PropTypes.func,
|
||||||
onPin: PropTypes.func,
|
onPin : PropTypes.func,
|
||||||
onEmbed: PropTypes.func,
|
onEmbed : PropTypes.func,
|
||||||
intl: PropTypes.object.isRequired,
|
intl : PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleReplyClick = () => {
|
handleReplyClick = () => {
|
||||||
this.props.onReply(this.props.status);
|
this.props.onReply(this.props.status);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleReblogClick = (e) => {
|
handleReblogClick = (e) => {
|
||||||
this.props.onReblog(this.props.status, e);
|
this.props.onReblog(this.props.status, e);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleFavouriteClick = () => {
|
handleFavouriteClick = () => {
|
||||||
this.props.onFavourite(this.props.status);
|
this.props.onFavourite(this.props.status);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleBookmarkClick = (e) => {
|
handleBookmarkClick = (e) => {
|
||||||
this.props.onBookmark(this.props.status, e);
|
this.props.onBookmark(this.props.status, e);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleDeleteClick = () => {
|
handleDeleteClick = () => {
|
||||||
this.props.onDelete(this.props.status, this.context.router.history);
|
this.props.onDelete(this.props.status, this.context.router.history);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleRedraftClick = () => {
|
handleRedraftClick = () => {
|
||||||
this.props.onDelete(this.props.status, this.context.router.history, true);
|
this.props.onDelete(this.props.status, this.context.router.history, true);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleDirectClick = () => {
|
handleDirectClick = () => {
|
||||||
this.props.onDirect(this.props.status.get('account'), this.context.router.history);
|
this.props.onDirect(this.props.status.get('account'), this.context.router.history);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMentionClick = () => {
|
handleMentionClick = () => {
|
||||||
this.props.onMention(this.props.status.get('account'), this.context.router.history);
|
this.props.onMention(this.props.status.get('account'), this.context.router.history);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMuteClick = () => {
|
handleMuteClick = () => {
|
||||||
const { status, relationship, onMute, onUnmute } = this.props;
|
const { status, relationship, onMute, onUnmute } = this.props;
|
||||||
|
@ -113,7 +113,7 @@ class ActionBar extends React.PureComponent {
|
||||||
} else {
|
} else {
|
||||||
onMute(account);
|
onMute(account);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleBlockClick = () => {
|
handleBlockClick = () => {
|
||||||
const { status, relationship, onBlock, onUnblock } = this.props;
|
const { status, relationship, onBlock, onUnblock } = this.props;
|
||||||
|
@ -124,50 +124,50 @@ class ActionBar extends React.PureComponent {
|
||||||
} else {
|
} else {
|
||||||
onBlock(status);
|
onBlock(status);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleBlockDomain = () => {
|
handleBlockDomain = () => {
|
||||||
const { status, onBlockDomain } = this.props;
|
const { status, onBlockDomain } = this.props;
|
||||||
const account = status.get('account');
|
const account = status.get('account');
|
||||||
|
|
||||||
onBlockDomain(account.get('acct').split('@')[1]);
|
onBlockDomain(account.get('acct').split('@')[1]);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleUnblockDomain = () => {
|
handleUnblockDomain = () => {
|
||||||
const { status, onUnblockDomain } = this.props;
|
const { status, onUnblockDomain } = this.props;
|
||||||
const account = status.get('account');
|
const account = status.get('account');
|
||||||
|
|
||||||
onUnblockDomain(account.get('acct').split('@')[1]);
|
onUnblockDomain(account.get('acct').split('@')[1]);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleConversationMuteClick = () => {
|
handleConversationMuteClick = () => {
|
||||||
this.props.onMuteConversation(this.props.status);
|
this.props.onMuteConversation(this.props.status);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleReport = () => {
|
handleReport = () => {
|
||||||
this.props.onReport(this.props.status);
|
this.props.onReport(this.props.status);
|
||||||
}
|
};
|
||||||
|
|
||||||
handlePinClick = () => {
|
handlePinClick = () => {
|
||||||
this.props.onPin(this.props.status);
|
this.props.onPin(this.props.status);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleShare = () => {
|
handleShare = () => {
|
||||||
navigator.share({
|
navigator.share({
|
||||||
text: this.props.status.get('search_index'),
|
text: this.props.status.get('search_index'),
|
||||||
url: this.props.status.get('url'),
|
url : this.props.status.get('url'),
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
handleEmbed = () => {
|
handleEmbed = () => {
|
||||||
this.props.onEmbed(this.props.status);
|
this.props.onEmbed(this.props.status);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleCopy = () => {
|
handleCopy = () => {
|
||||||
const url = this.props.status.get('url');
|
const url = this.props.status.get('url');
|
||||||
const textarea = document.createElement('textarea');
|
const textarea = document.createElement('textarea');
|
||||||
|
|
||||||
textarea.textContent = url;
|
textarea.textContent = url;
|
||||||
textarea.style.position = 'fixed';
|
textarea.style.position = 'fixed';
|
||||||
|
|
||||||
document.body.appendChild(textarea);
|
document.body.appendChild(textarea);
|
||||||
|
@ -180,14 +180,14 @@ class ActionBar extends React.PureComponent {
|
||||||
} finally {
|
} finally {
|
||||||
document.body.removeChild(textarea);
|
document.body.removeChild(textarea);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { status, relationship, intl } = this.props;
|
const { status, relationship, intl } = this.props;
|
||||||
|
|
||||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||||
const mutingConversation = status.get('muted');
|
const mutingConversation = status.get('muted');
|
||||||
const account = status.get('account');
|
const account = status.get('account');
|
||||||
|
|
||||||
let menu = [];
|
let menu = [];
|
||||||
|
|
||||||
|
@ -199,36 +199,66 @@ class ActionBar extends React.PureComponent {
|
||||||
|
|
||||||
if (me === status.getIn(['account', 'id'])) {
|
if (me === status.getIn(['account', 'id'])) {
|
||||||
if (publicStatus) {
|
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 {
|
} else {
|
||||||
if (status.get('visibility') === 'private') {
|
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(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(null);
|
||||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
|
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
|
||||||
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
|
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
menu.push({
|
||||||
menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
|
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);
|
menu.push(null);
|
||||||
|
|
||||||
if (relationship && relationship.get('muting')) {
|
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 {
|
} 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')) {
|
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 {
|
} 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')) {
|
if (account.get('acct') !== account.get('username')) {
|
||||||
const domain = account.get('acct').split('@')[1];
|
const domain = account.get('acct').split('@')[1];
|
||||||
|
@ -244,13 +274,23 @@ class ActionBar extends React.PureComponent {
|
||||||
|
|
||||||
if (isStaff) {
|
if (isStaff) {
|
||||||
menu.push(null);
|
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({
|
||||||
menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
|
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' && (
|
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;
|
let replyIcon;
|
||||||
|
@ -268,16 +308,48 @@ class ActionBar extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='detailed-status__action-bar'>
|
<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
|
||||||
<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>
|
title={intl.formatMessage(messages.reply)}
|
||||||
<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>
|
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}
|
{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'>
|
<div className='detailed-status__action-bar-dropdown'>
|
||||||
<DropdownMenuContainer size={18} icon='ellipsis-h' status={status} items={menu} direction='left' title='More' />
|
<DropdownMenuContainer
|
||||||
</div>
|
size={18}
|
||||||
</div>
|
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 classNames from 'classnames';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { createSelector } from 'reselect';
|
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 MissingIndicator from '../../components/missing_indicator';
|
||||||
import DetailedStatus from './components/detailed_status';
|
import DetailedStatus from './components/detailed_status';
|
||||||
import ActionBar from './components/action_bar';
|
import ActionBar from './components/action_bar';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../ui/components/column';
|
||||||
import {
|
import {
|
||||||
favourite,
|
|
||||||
unfavourite,
|
|
||||||
bookmark,
|
bookmark,
|
||||||
unbookmark,
|
favourite,
|
||||||
reblog,
|
|
||||||
unreblog,
|
|
||||||
pin,
|
pin,
|
||||||
|
reblog,
|
||||||
|
unbookmark,
|
||||||
|
unfavourite,
|
||||||
unpin,
|
unpin,
|
||||||
|
unreblog,
|
||||||
} from '../../actions/interactions';
|
} from '../../actions/interactions';
|
||||||
import {
|
import { directCompose, mentionCompose, replyCompose } from '../../actions/compose';
|
||||||
replyCompose,
|
import { unblockAccount, unmuteAccount } from '../../actions/accounts';
|
||||||
mentionCompose,
|
import { blockDomain, unblockDomain } from '../../actions/domain_blocks';
|
||||||
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 { initMuteModal } from '../../actions/mutes';
|
import { initMuteModal } from '../../actions/mutes';
|
||||||
import { initBlockModal } from '../../actions/blocks';
|
import { initBlockModal } from '../../actions/blocks';
|
||||||
import { initReport } from '../../actions/reports';
|
import { initReport } from '../../actions/reports';
|
||||||
|
@ -49,24 +32,33 @@ import ColumnBackButton from '../../components/column_back_button';
|
||||||
import ColumnHeader from '../../components/column_header';
|
import ColumnHeader from '../../components/column_header';
|
||||||
import StatusContainer from '../../containers/status_container';
|
import StatusContainer from '../../containers/status_container';
|
||||||
import { openModal } from '../../actions/modal';
|
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 ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { HotKeys } from 'react-hotkeys';
|
import { HotKeys } from 'react-hotkeys';
|
||||||
import { boostModal, deleteModal } from '../../initial_state';
|
import { boostModal, deleteModal } from '../../initial_state';
|
||||||
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen';
|
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';
|
import Icon from 'mastodon/components/icon';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
deleteConfirm : { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
||||||
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
|
deleteMessage : {
|
||||||
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
|
id : 'confirmations.delete.message',
|
||||||
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.' },
|
defaultMessage: 'Are you sure you want to delete this status?',
|
||||||
revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
|
},
|
||||||
hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
|
redraftConfirm : { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
|
||||||
detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
|
redraftMessage : {
|
||||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
id : 'confirmations.redraft.message',
|
||||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
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?',
|
||||||
|
},
|
||||||
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -99,7 +91,7 @@ const makeMapStateToProps = () => {
|
||||||
const ids = [statusId];
|
const ids = [statusId];
|
||||||
|
|
||||||
while (ids.length > 0) {
|
while (ids.length > 0) {
|
||||||
let id = ids.shift();
|
let id = ids.shift();
|
||||||
const replies = contextReplies.get(id);
|
const replies = contextReplies.get(id);
|
||||||
|
|
||||||
if (statusId !== id) {
|
if (statusId !== id) {
|
||||||
|
@ -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'));
|
let insertAt = descendantsIds.findIndex((id) => statuses.get(id).get('in_reply_to_account_id') !== statuses.get(id).get('account'));
|
||||||
if (insertAt !== -1) {
|
if (insertAt !== -1) {
|
||||||
descendantsIds.forEach((id, idx) => {
|
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(idx, 1);
|
||||||
descendantsIds.splice(insertAt, 0, id);
|
descendantsIds.splice(insertAt, 0, id);
|
||||||
insertAt += 1;
|
insertAt += 1;
|
||||||
|
@ -142,7 +135,7 @@ const makeMapStateToProps = () => {
|
||||||
ancestorsIds,
|
ancestorsIds,
|
||||||
descendantsIds,
|
descendantsIds,
|
||||||
askReplyConfirmation: state.getIn(['compose', 'text']).trim().length !== 0,
|
askReplyConfirmation: state.getIn(['compose', 'text']).trim().length !== 0,
|
||||||
domain: state.getIn(['meta', 'domain']),
|
domain : state.getIn(['meta', 'domain']),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -158,45 +151,48 @@ class Status extends ImmutablePureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params : PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch : PropTypes.func.isRequired,
|
||||||
status: ImmutablePropTypes.map,
|
status : ImmutablePropTypes.map,
|
||||||
ancestorsIds: ImmutablePropTypes.list,
|
ancestorsIds : ImmutablePropTypes.list,
|
||||||
descendantsIds: ImmutablePropTypes.list,
|
descendantsIds : ImmutablePropTypes.list,
|
||||||
intl: PropTypes.object.isRequired,
|
intl : PropTypes.object.isRequired,
|
||||||
askReplyConfirmation: PropTypes.bool,
|
askReplyConfirmation: PropTypes.bool,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn : PropTypes.bool,
|
||||||
domain: PropTypes.string.isRequired,
|
domain : PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
fullscreen: false,
|
fullscreen : false,
|
||||||
showMedia: defaultMediaVisibility(this.props.status),
|
showMedia : defaultMediaVisibility(this.props.status),
|
||||||
loadedStatusId: undefined,
|
loadedStatusId: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount() {
|
||||||
this.props.dispatch(fetchStatus(this.props.params.statusId));
|
this.props.dispatch(fetchStatus(this.props.params.statusId));
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
attachFullscreenListener(this.onFullScreenChange);
|
attachFullscreenListener(this.onFullScreenChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
|
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
|
||||||
this._scrolledIntoView = false;
|
this._scrolledIntoView = false;
|
||||||
this.props.dispatch(fetchStatus(nextProps.params.statusId));
|
this.props.dispatch(fetchStatus(nextProps.params.statusId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) {
|
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 = () => {
|
handleToggleMediaVisibility = () => {
|
||||||
this.setState({ showMedia: !this.state.showMedia });
|
this.setState({ showMedia: !this.state.showMedia });
|
||||||
}
|
};
|
||||||
|
|
||||||
handleFavouriteClick = (status) => {
|
handleFavouriteClick = (status) => {
|
||||||
if (status.get('favourited')) {
|
if (status.get('favourited')) {
|
||||||
|
@ -204,7 +200,7 @@ class Status extends ImmutablePureComponent {
|
||||||
} else {
|
} else {
|
||||||
this.props.dispatch(favourite(status));
|
this.props.dispatch(favourite(status));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handlePin = (status) => {
|
handlePin = (status) => {
|
||||||
if (status.get('pinned')) {
|
if (status.get('pinned')) {
|
||||||
|
@ -212,24 +208,24 @@ class Status extends ImmutablePureComponent {
|
||||||
} else {
|
} else {
|
||||||
this.props.dispatch(pin(status));
|
this.props.dispatch(pin(status));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleReplyClick = (status) => {
|
handleReplyClick = (status) => {
|
||||||
let { askReplyConfirmation, dispatch, intl } = this.props;
|
let { askReplyConfirmation, dispatch, intl } = this.props;
|
||||||
if (askReplyConfirmation) {
|
if (askReplyConfirmation) {
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
message: intl.formatMessage(messages.replyMessage),
|
message : intl.formatMessage(messages.replyMessage),
|
||||||
confirm: intl.formatMessage(messages.replyConfirm),
|
confirm : intl.formatMessage(messages.replyConfirm),
|
||||||
onConfirm: () => dispatch(replyCompose(status, this.context.router.history)),
|
onConfirm: () => dispatch(replyCompose(status, this.context.router.history)),
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
dispatch(replyCompose(status, this.context.router.history));
|
dispatch(replyCompose(status, this.context.router.history));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleModalReblog = (status) => {
|
handleModalReblog = (status) => {
|
||||||
this.props.dispatch(reblog(status));
|
this.props.dispatch(reblog(status));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleReblogClick = (status, e) => {
|
handleReblogClick = (status, e) => {
|
||||||
if (status.get('reblogged')) {
|
if (status.get('reblogged')) {
|
||||||
|
@ -241,7 +237,7 @@ class Status extends ImmutablePureComponent {
|
||||||
this.props.dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
|
this.props.dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleBookmarkClick = (status) => {
|
handleBookmarkClick = (status) => {
|
||||||
if (status.get('bookmarked')) {
|
if (status.get('bookmarked')) {
|
||||||
|
@ -249,7 +245,7 @@ class Status extends ImmutablePureComponent {
|
||||||
} else {
|
} else {
|
||||||
this.props.dispatch(bookmark(status));
|
this.props.dispatch(bookmark(status));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleDeleteClick = (status, history, withRedraft = false) => {
|
handleDeleteClick = (status, history, withRedraft = false) => {
|
||||||
const { dispatch, intl } = this.props;
|
const { dispatch, intl } = this.props;
|
||||||
|
@ -258,28 +254,28 @@ class Status extends ImmutablePureComponent {
|
||||||
dispatch(deleteStatus(status.get('id'), history, withRedraft));
|
dispatch(deleteStatus(status.get('id'), history, withRedraft));
|
||||||
} else {
|
} else {
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
|
message : intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
|
||||||
confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
|
confirm : intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
|
||||||
onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
|
onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleDirectClick = (account, router) => {
|
handleDirectClick = (account, router) => {
|
||||||
this.props.dispatch(directCompose(account, router));
|
this.props.dispatch(directCompose(account, router));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMentionClick = (account, router) => {
|
handleMentionClick = (account, router) => {
|
||||||
this.props.dispatch(mentionCompose(account, router));
|
this.props.dispatch(mentionCompose(account, router));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleOpenMedia = (media, index) => {
|
handleOpenMedia = (media, index) => {
|
||||||
this.props.dispatch(openModal('MEDIA', { media, index }));
|
this.props.dispatch(openModal('MEDIA', { media, index }));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleOpenVideo = (media, time) => {
|
handleOpenVideo = (media, time) => {
|
||||||
this.props.dispatch(openModal('VIDEO', { media, time }));
|
this.props.dispatch(openModal('VIDEO', { media, time }));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyOpenMedia = e => {
|
handleHotkeyOpenMedia = e => {
|
||||||
const status = this._properStatus();
|
const status = this._properStatus();
|
||||||
|
@ -295,11 +291,11 @@ class Status extends ImmutablePureComponent {
|
||||||
this.handleOpenMedia(status.get('media_attachments'), 0);
|
this.handleOpenMedia(status.get('media_attachments'), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMuteClick = (account) => {
|
handleMuteClick = (account) => {
|
||||||
this.props.dispatch(initMuteModal(account));
|
this.props.dispatch(initMuteModal(account));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleConversationMuteClick = (status) => {
|
handleConversationMuteClick = (status) => {
|
||||||
if (status.get('muted')) {
|
if (status.get('muted')) {
|
||||||
|
@ -307,7 +303,7 @@ class Status extends ImmutablePureComponent {
|
||||||
} else {
|
} else {
|
||||||
this.props.dispatch(muteStatus(status.get('id')));
|
this.props.dispatch(muteStatus(status.get('id')));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleToggleHidden = (status) => {
|
handleToggleHidden = (status) => {
|
||||||
if (status.get('hidden')) {
|
if (status.get('hidden')) {
|
||||||
|
@ -315,7 +311,7 @@ class Status extends ImmutablePureComponent {
|
||||||
} else {
|
} else {
|
||||||
this.props.dispatch(hideStatus(status.get('id')));
|
this.props.dispatch(hideStatus(status.get('id')));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleToggleAll = () => {
|
handleToggleAll = () => {
|
||||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||||
|
@ -326,80 +322,83 @@ class Status extends ImmutablePureComponent {
|
||||||
} else {
|
} else {
|
||||||
this.props.dispatch(hideStatus(statusIds));
|
this.props.dispatch(hideStatus(statusIds));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleBlockClick = (status) => {
|
handleBlockClick = (status) => {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
const account = status.get('account');
|
const account = status.get('account');
|
||||||
dispatch(initBlockModal(account));
|
dispatch(initBlockModal(account));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleReport = (status) => {
|
handleReport = (status) => {
|
||||||
this.props.dispatch(initReport(status.get('account'), status));
|
this.props.dispatch(initReport(status.get('account'), status));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleEmbed = (status) => {
|
handleEmbed = (status) => {
|
||||||
this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
|
this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleUnmuteClick = account => {
|
handleUnmuteClick = account => {
|
||||||
this.props.dispatch(unmuteAccount(account.get('id')));
|
this.props.dispatch(unmuteAccount(account.get('id')));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleUnblockClick = account => {
|
handleUnblockClick = account => {
|
||||||
this.props.dispatch(unblockAccount(account.get('id')));
|
this.props.dispatch(unblockAccount(account.get('id')));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleBlockDomainClick = domain => {
|
handleBlockDomainClick = domain => {
|
||||||
this.props.dispatch(openModal('CONFIRM', {
|
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
|
||||||
confirm: this.props.intl.formatMessage(messages.blockDomainConfirm),
|
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)),
|
onConfirm: () => this.props.dispatch(blockDomain(domain)),
|
||||||
}));
|
}));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleUnblockDomainClick = domain => {
|
handleUnblockDomainClick = domain => {
|
||||||
this.props.dispatch(unblockDomain(domain));
|
this.props.dispatch(unblockDomain(domain));
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
handleHotkeyMoveUp = () => {
|
handleHotkeyMoveUp = () => {
|
||||||
this.handleMoveUp(this.props.status.get('id'));
|
this.handleMoveUp(this.props.status.get('id'));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyMoveDown = () => {
|
handleHotkeyMoveDown = () => {
|
||||||
this.handleMoveDown(this.props.status.get('id'));
|
this.handleMoveDown(this.props.status.get('id'));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyReply = e => {
|
handleHotkeyReply = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.handleReplyClick(this.props.status);
|
this.handleReplyClick(this.props.status);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyFavourite = () => {
|
handleHotkeyFavourite = () => {
|
||||||
this.handleFavouriteClick(this.props.status);
|
this.handleFavouriteClick(this.props.status);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyBoost = () => {
|
handleHotkeyBoost = () => {
|
||||||
this.handleReblogClick(this.props.status);
|
this.handleReblogClick(this.props.status);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyMention = e => {
|
handleHotkeyMention = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.handleMentionClick(this.props.status.get('account'));
|
this.handleMentionClick(this.props.status.get('account'));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyOpenProfile = () => {
|
handleHotkeyOpenProfile = () => {
|
||||||
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
|
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyToggleHidden = () => {
|
handleHotkeyToggleHidden = () => {
|
||||||
this.handleToggleHidden(this.props.status);
|
this.handleToggleHidden(this.props.status);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyToggleSensitive = () => {
|
handleHotkeyToggleSensitive = () => {
|
||||||
this.handleToggleMediaVisibility();
|
this.handleToggleMediaVisibility();
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMoveUp = id => {
|
handleMoveUp = id => {
|
||||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||||
|
@ -416,7 +415,7 @@ class Status extends ImmutablePureComponent {
|
||||||
this._selectChild(index - 1, true);
|
this._selectChild(index - 1, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMoveDown = id => {
|
handleMoveDown = id => {
|
||||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||||
|
@ -433,9 +432,9 @@ class Status extends ImmutablePureComponent {
|
||||||
this._selectChild(index + 1, false);
|
this._selectChild(index + 1, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
_selectChild (index, align_top) {
|
_selectChild(index, align_top) {
|
||||||
const container = this.node;
|
const container = this.node;
|
||||||
const element = container.querySelectorAll('.focusable')[index];
|
const element = container.querySelectorAll('.focusable')[index];
|
||||||
|
|
||||||
|
@ -449,7 +448,7 @@ class Status extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChildren (list) {
|
renderChildren(list) {
|
||||||
return list.map(id => (
|
return list.map(id => (
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
key={id}
|
key={id}
|
||||||
|
@ -463,9 +462,9 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
setRef = c => {
|
setRef = c => {
|
||||||
this.node = c;
|
this.node = c;
|
||||||
}
|
};
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate() {
|
||||||
if (this._scrolledIntoView) {
|
if (this._scrolledIntoView) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -482,65 +481,84 @@ class Status extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
detachFullscreenListener(this.onFullScreenChange);
|
detachFullscreenListener(this.onFullScreenChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
onFullScreenChange = () => {
|
onFullScreenChange = () => {
|
||||||
this.setState({ fullscreen: isFullscreen() });
|
this.setState({ fullscreen: isFullscreen() });
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
let ancestors, descendants;
|
let ancestors, descendants;
|
||||||
const { shouldUpdateScroll, status, ancestorsIds, descendantsIds, intl, domain, multiColumn } = this.props;
|
const { shouldUpdateScroll, status, ancestorsIds, descendantsIds, intl, domain, multiColumn } = this.props;
|
||||||
const { fullscreen } = this.state;
|
const { fullscreen } = this.state;
|
||||||
|
|
||||||
if (status === null) {
|
if (status === null) {
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column >
|
||||||
<ColumnBackButton multiColumn={multiColumn} />
|
<ColumnBackButton multiColumn={multiColumn} />
|
||||||
<MissingIndicator />
|
<MissingIndicator />
|
||||||
</Column>
|
</Column >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ancestorsIds && ancestorsIds.size > 0) {
|
if (ancestorsIds && ancestorsIds.size > 0) {
|
||||||
ancestors = <div>{this.renderChildren(ancestorsIds)}</div>;
|
ancestors = <div >{this.renderChildren(ancestorsIds)}</div >;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descendantsIds && descendantsIds.size > 0) {
|
if (descendantsIds && descendantsIds.size > 0) {
|
||||||
descendants = <div>{this.renderChildren(descendantsIds)}</div>;
|
descendants = <div >{this.renderChildren(descendantsIds)}</div >;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
moveUp: this.handleHotkeyMoveUp,
|
moveUp : this.handleHotkeyMoveUp,
|
||||||
moveDown: this.handleHotkeyMoveDown,
|
moveDown : this.handleHotkeyMoveDown,
|
||||||
reply: this.handleHotkeyReply,
|
reply : this.handleHotkeyReply,
|
||||||
favourite: this.handleHotkeyFavourite,
|
favourite : this.handleHotkeyFavourite,
|
||||||
boost: this.handleHotkeyBoost,
|
boost : this.handleHotkeyBoost,
|
||||||
mention: this.handleHotkeyMention,
|
mention : this.handleHotkeyMention,
|
||||||
openProfile: this.handleHotkeyOpenProfile,
|
openProfile : this.handleHotkeyOpenProfile,
|
||||||
toggleHidden: this.handleHotkeyToggleHidden,
|
toggleHidden : this.handleHotkeyToggleHidden,
|
||||||
toggleSensitive: this.handleHotkeyToggleSensitive,
|
toggleSensitive: this.handleHotkeyToggleSensitive,
|
||||||
openMedia: this.handleHotkeyOpenMedia,
|
openMedia : this.handleHotkeyOpenMedia,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.detailedStatus)}>
|
<Column
|
||||||
|
bindToDocument={!multiColumn}
|
||||||
|
label={intl.formatMessage(messages.detailedStatus)}
|
||||||
|
>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
showBackButton
|
showBackButton
|
||||||
multiColumn={multiColumn}
|
multiColumn={multiColumn}
|
||||||
extraButton={(
|
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}>
|
<ScrollContainer
|
||||||
<div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
|
scrollKey='thread'
|
||||||
|
shouldUpdateScroll={shouldUpdateScroll}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={classNames('scrollable', { fullscreen })}
|
||||||
|
ref={this.setRef}
|
||||||
|
>
|
||||||
{ancestors}
|
{ancestors}
|
||||||
|
|
||||||
<HotKeys handlers={handlers}>
|
<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
|
<DetailedStatus
|
||||||
status={status}
|
status={status}
|
||||||
onOpenVideo={this.handleOpenVideo}
|
onOpenVideo={this.handleOpenVideo}
|
||||||
|
@ -571,13 +589,13 @@ class Status extends ImmutablePureComponent {
|
||||||
onPin={this.handlePin}
|
onPin={this.handlePin}
|
||||||
onEmbed={this.handleEmbed}
|
onEmbed={this.handleEmbed}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div >
|
||||||
</HotKeys>
|
</HotKeys >
|
||||||
|
|
||||||
{descendants}
|
{descendants}
|
||||||
</div>
|
</div >
|
||||||
</ScrollContainer>
|
</ScrollContainer >
|
||||||
</Column>
|
</Column >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,75 +46,123 @@ class LinkFooter extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='getting-started__footer'>
|
<div className='getting-started__footer'>
|
||||||
<ul>
|
<ul >
|
||||||
<li>
|
<li >
|
||||||
<a href="https://liberapay.com/cipherbliss">Supportez Cipherbliss</a>
|
<a href='https://liberapay.com/cipherbliss'>Supportez Cipherbliss</a >
|
||||||
</li>
|
</li >
|
||||||
<li>
|
<li >
|
||||||
<a href="/@tykayn">
|
<a href='https://mastodon.cipherbliss.com/@tykayn'>
|
||||||
<i className="fa fa-envelope"></i>
|
<i className='fa fa-paper-plane' />
|
||||||
contactez Cipherbliss</a>
|
contactez nous</a >
|
||||||
</li>
|
</li >
|
||||||
<li>
|
<li >
|
||||||
|
|
||||||
<a href='/admin/tags?pending_review=1'>
|
<a href='/admin/tags?pending_review=1'>
|
||||||
<i className="fa fa-fire"></i>
|
<i className='fa fa-fire' />
|
||||||
Trending hashtags</a>
|
Trending hashtags</a >
|
||||||
<hr/>
|
<hr />
|
||||||
</li>
|
</li >
|
||||||
{invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage
|
{invitesEnabled && <li ><a
|
||||||
|
href='/invites'
|
||||||
|
target='_blank'
|
||||||
|
><FormattedMessage
|
||||||
id='getting_started.invite'
|
id='getting_started.invite'
|
||||||
defaultMessage='Invite people'
|
defaultMessage='Invite people'
|
||||||
/></a> ·
|
/> ·</a >
|
||||||
</li>}
|
</li >}
|
||||||
{withHotkeys && <li><Link to='/keyboard-shortcuts'><FormattedMessage
|
{withHotkeys && <li ><Link to='/keyboard-shortcuts'>
|
||||||
id='navigation_bar.keyboard_shortcuts'
|
<FormattedMessage
|
||||||
defaultMessage='Hotkeys'
|
id='navigation_bar.keyboard_shortcuts'
|
||||||
/></Link> ·
|
defaultMessage='Hotkeys'
|
||||||
</li>}
|
/> ·
|
||||||
<li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security'/></a> ·
|
</Link >
|
||||||
</li>
|
</li >}
|
||||||
<li><a href='/about/more' target='_blank'><FormattedMessage
|
<li >
|
||||||
id='navigation_bar.info'
|
<a href='/auth/edit'>
|
||||||
defaultMessage='About this server'
|
<FormattedMessage
|
||||||
/></a> ·
|
id='getting_started.security'
|
||||||
</li>
|
defaultMessage='Security'
|
||||||
<li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage
|
/> ·
|
||||||
id='navigation_bar.apps'
|
</a >
|
||||||
defaultMessage='Mobile apps'
|
</li >
|
||||||
/></a> ·
|
<li >
|
||||||
</li>
|
<a
|
||||||
<li><a href='/terms' target='_blank'><FormattedMessage
|
href='/about/more'
|
||||||
id='getting_started.terms'
|
target='_blank'
|
||||||
defaultMessage='Terms of service'
|
><FormattedMessage
|
||||||
/></a> ·
|
id='navigation_bar.info'
|
||||||
</li>
|
defaultMessage='About this server'
|
||||||
<li><a href='/settings/applications' target='_blank'><FormattedMessage
|
/> ·
|
||||||
id='getting_started.developers'
|
</a >
|
||||||
defaultMessage='Developers'
|
</li >
|
||||||
/></a> ·
|
<li >
|
||||||
</li>
|
<a
|
||||||
<li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage
|
href='https://joinmastodon.org/apps'
|
||||||
id='getting_started.documentation' defaultMessage='Documentation'
|
target='_blank'
|
||||||
/></a> ·
|
><FormattedMessage
|
||||||
</li>
|
id='navigation_bar.apps'
|
||||||
<li><a href='/auth/sign_out' onClick={this.handleLogoutClick}><FormattedMessage
|
defaultMessage='Mobile apps'
|
||||||
id='navigation_bar.logout'
|
/> ·
|
||||||
defaultMessage='Logout'
|
</a >
|
||||||
/></a>
|
</li >
|
||||||
</li>
|
<li >
|
||||||
</ul>
|
<a
|
||||||
|
href='/terms'
|
||||||
|
target='_blank'
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='getting_started.terms'
|
||||||
|
defaultMessage='Terms of service'
|
||||||
|
/> ·</a >
|
||||||
|
</li >
|
||||||
|
<li >
|
||||||
|
<a
|
||||||
|
href='/settings/applications'
|
||||||
|
target='_blank'
|
||||||
|
><FormattedMessage
|
||||||
|
id='getting_started.developers'
|
||||||
|
defaultMessage='Developers'
|
||||||
|
/> ·
|
||||||
|
</a >
|
||||||
|
</li >
|
||||||
|
<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
|
||||||
|
id='navigation_bar.logout'
|
||||||
|
defaultMessage='Logout'
|
||||||
|
/>
|
||||||
|
</a >
|
||||||
|
</li >
|
||||||
|
</ul >
|
||||||
|
|
||||||
<p>
|
<p >
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='getting_started.open_source_notice'
|
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={{
|
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>
|
</p >
|
||||||
</div>
|
</div >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,158 +14,214 @@ const NavigationPanel = () => (
|
||||||
<div className='navigation-panel'>
|
<div className='navigation-panel'>
|
||||||
|
|
||||||
<NavLink
|
<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'
|
data-preview-icon='home'
|
||||||
><Icon className='column-link__icon' id='home' fixedWidth/><FormattedMessage
|
><Icon
|
||||||
id='tabs_bar.home' defaultMessage='Home'
|
className='column-link__icon'
|
||||||
/></NavLink>
|
id='home'
|
||||||
|
fixedWidth
|
||||||
|
/><FormattedMessage
|
||||||
|
id='tabs_bar.home'
|
||||||
|
defaultMessage='Home'
|
||||||
|
/></NavLink >
|
||||||
<NavLink
|
<NavLink
|
||||||
className='column-link column-link--transparent' to='/notifications'
|
className='column-link column-link--transparent'
|
||||||
data-preview-title-id='column.notifications' data-preview-icon='bell'
|
to='/notifications'
|
||||||
|
data-preview-title-id='column.notifications'
|
||||||
|
data-preview-icon='bell'
|
||||||
><NotificationsCounterIcon
|
><NotificationsCounterIcon
|
||||||
className='column-link__icon'
|
className='column-link__icon'
|
||||||
/><FormattedMessage
|
/><FormattedMessage
|
||||||
id='tabs_bar.notifications'
|
id='tabs_bar.notifications'
|
||||||
defaultMessage='Notifications'
|
defaultMessage='Notifications'
|
||||||
/></NavLink>
|
/></NavLink >
|
||||||
<FollowRequestsNavLink/>
|
<FollowRequestsNavLink />
|
||||||
<NavLink
|
<NavLink
|
||||||
className='column-link column-link--transparent' to='/timelines/public/local'
|
className='column-link column-link--transparent'
|
||||||
data-preview-title-id='column.community' data-preview-icon='users'
|
to='/timelines/public/local'
|
||||||
|
data-preview-title-id='column.community'
|
||||||
|
data-preview-icon='users'
|
||||||
><Icon
|
><Icon
|
||||||
className='column-link__icon'
|
className='column-link__icon'
|
||||||
id='users'
|
id='users'
|
||||||
fixedWidth
|
fixedWidth
|
||||||
/><FormattedMessage
|
/><FormattedMessage
|
||||||
id='tabs_bar.local_timeline' defaultMessage='Local'
|
id='tabs_bar.local_timeline'
|
||||||
/></NavLink>
|
defaultMessage='Local'
|
||||||
|
/></NavLink >
|
||||||
<NavLink
|
<NavLink
|
||||||
className='column-link column-link--transparent' exact to='/timelines/public'
|
className='column-link column-link--transparent'
|
||||||
data-preview-title-id='column.public' data-preview-icon='globe'
|
exact
|
||||||
|
to='/timelines/public'
|
||||||
|
data-preview-title-id='column.public'
|
||||||
|
data-preview-icon='globe'
|
||||||
><Icon
|
><Icon
|
||||||
className='column-link__icon'
|
className='column-link__icon'
|
||||||
id='globe'
|
id='globe'
|
||||||
fixedWidth
|
fixedWidth
|
||||||
/><FormattedMessage
|
/><FormattedMessage
|
||||||
id='tabs_bar.federated_timeline' defaultMessage='Federated'
|
id='tabs_bar.federated_timeline'
|
||||||
/></NavLink>
|
defaultMessage='Federated'
|
||||||
<NavLink className='column-link column-link--transparent' to='/timelines/direct'><Icon
|
/></NavLink >
|
||||||
|
<NavLink
|
||||||
|
className='column-link column-link--transparent'
|
||||||
|
to='/timelines/direct'
|
||||||
|
><Icon
|
||||||
className='column-link__icon'
|
className='column-link__icon'
|
||||||
id='envelope'
|
id='envelope'
|
||||||
fixedWidth
|
fixedWidth
|
||||||
/><FormattedMessage
|
/><FormattedMessage
|
||||||
id='navigation_bar.direct' defaultMessage='Direct messages'
|
id='navigation_bar.direct'
|
||||||
/></NavLink>
|
defaultMessage='Direct messages'
|
||||||
<NavLink className='column-link column-link--transparent' to='/favourites'><Icon
|
/></NavLink >
|
||||||
|
<NavLink
|
||||||
|
className='column-link column-link--transparent'
|
||||||
|
to='/favourites'
|
||||||
|
><Icon
|
||||||
className='column-link__icon'
|
className='column-link__icon'
|
||||||
id='star'
|
id='star'
|
||||||
fixedWidth
|
fixedWidth
|
||||||
/><FormattedMessage
|
/><FormattedMessage
|
||||||
id='navigation_bar.favourites' defaultMessage='Favourites'
|
id='navigation_bar.favourites'
|
||||||
/></NavLink>
|
defaultMessage='Favourites'
|
||||||
<NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon
|
/></NavLink >
|
||||||
|
<NavLink
|
||||||
|
className='column-link column-link--transparent'
|
||||||
|
to='/bookmarks'
|
||||||
|
><Icon
|
||||||
className='column-link__icon'
|
className='column-link__icon'
|
||||||
id='bookmark'
|
id='bookmark'
|
||||||
fixedWidth
|
fixedWidth
|
||||||
/><FormattedMessage
|
/><FormattedMessage
|
||||||
id='navigation_bar.bookmarks' defaultMessage='Bookmarks'
|
id='navigation_bar.bookmarks'
|
||||||
/></NavLink>
|
defaultMessage='Bookmarks'
|
||||||
<NavLink className='column-link column-link--transparent' to='/lists'><Icon
|
/></NavLink >
|
||||||
|
<NavLink
|
||||||
|
className='column-link column-link--transparent'
|
||||||
|
to='/lists'
|
||||||
|
><Icon
|
||||||
className='column-link__icon'
|
className='column-link__icon'
|
||||||
id='list-ul'
|
id='list-ul'
|
||||||
fixedWidth
|
fixedWidth
|
||||||
/><FormattedMessage
|
/><FormattedMessage
|
||||||
id='navigation_bar.lists' defaultMessage='Lists'
|
id='navigation_bar.lists'
|
||||||
/></NavLink>
|
defaultMessage='Lists'
|
||||||
|
/></NavLink >
|
||||||
{profile_directory &&
|
{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'
|
className='column-link__icon'
|
||||||
id='address-book-o'
|
id='address-book-o'
|
||||||
fixedWidth
|
fixedWidth
|
||||||
/><FormattedMessage
|
/><FormattedMessage
|
||||||
id='getting_started.directory' defaultMessage='Profile directory'
|
id='getting_started.directory'
|
||||||
/></NavLink>}
|
defaultMessage='Profile directory'
|
||||||
|
/></NavLink >}
|
||||||
|
|
||||||
<ListPanel/>
|
<ListPanel />
|
||||||
|
|
||||||
<hr/>
|
<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'
|
className='column-link__icon'
|
||||||
id='cog'
|
id='cog'
|
||||||
fixedWidth
|
fixedWidth
|
||||||
/><FormattedMessage
|
/><FormattedMessage
|
||||||
id='navigation_bar.preferences' defaultMessage='Preferences'
|
id='navigation_bar.preferences'
|
||||||
/></a>
|
defaultMessage='Preferences'
|
||||||
<a className='column-link column-link--transparent' href='/relationships'><Icon
|
/></a >
|
||||||
|
<a
|
||||||
|
className='column-link column-link--transparent'
|
||||||
|
href='/relationships'
|
||||||
|
><Icon
|
||||||
className='column-link__icon'
|
className='column-link__icon'
|
||||||
id='users'
|
id='users'
|
||||||
fixedWidth
|
fixedWidth
|
||||||
/><FormattedMessage
|
/><FormattedMessage
|
||||||
id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers'
|
id='navigation_bar.follows_and_followers'
|
||||||
/></a>
|
defaultMessage='Follows and followers'
|
||||||
|
/></a >
|
||||||
|
|
||||||
{showTrends && <div className='flex-spacer'/>}
|
{showTrends && <div className='flex-spacer' />}
|
||||||
{showTrends && <TrendsContainer/>}
|
{showTrends && <TrendsContainer />}
|
||||||
|
|
||||||
<div className='messaging-box'>
|
<div className='messaging-box'>
|
||||||
<div className='title'>
|
<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
|
Messaging box
|
||||||
</div>
|
</div >
|
||||||
<div className='user-list column-header'>
|
<div className='user-list column-header'>
|
||||||
<h2 className='title'>User list</h2>
|
<h2 className='title'>User list</h2 >
|
||||||
<ul>
|
<ul >
|
||||||
<li>
|
<li >
|
||||||
someone is logged in, click on me
|
someone is logged in, click on me
|
||||||
</li>
|
</li >
|
||||||
<li>
|
<li >
|
||||||
wulfila is here, click on me
|
wulfila is here, click on me
|
||||||
</li>
|
</li >
|
||||||
<li>
|
<li >
|
||||||
chuck norris is here, click on me
|
chuck norris is here, click on me
|
||||||
</li>
|
</li >
|
||||||
</ul>
|
</ul >
|
||||||
</div>
|
</div >
|
||||||
|
|
||||||
</div>
|
</div >
|
||||||
<div className='conversations_list'>
|
<div className='conversations_list'>
|
||||||
<ul>
|
<ul >
|
||||||
<li className='conversations_item has-new-message'>
|
<li className='conversations_item has-new-message'>
|
||||||
<div className='title'>
|
<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
|
Un Gens
|
||||||
<span className='new-message-counter'>
|
<span className='new-message-counter'>
|
||||||
(3)</span>
|
(3)</span >
|
||||||
<button className='btn-small'>
|
<button className='btn-small'>
|
||||||
<i role='img' className='fa fa-caret-down column-header__icon fa-fw'/>
|
<i
|
||||||
</button>
|
role='img'
|
||||||
</div>
|
className='fa fa-caret-down column-header__icon fa-fw'
|
||||||
|
/>
|
||||||
|
</button >
|
||||||
|
</div >
|
||||||
<div className='conversation_stream'>
|
<div className='conversation_stream'>
|
||||||
<div className='message theirs'>
|
<div className='message theirs'>
|
||||||
<p>oh hello there! 😋 </p>
|
<p >oh hello there! 😋 </p >
|
||||||
</div>
|
<div className='arrow-down' />
|
||||||
|
</div >
|
||||||
<div className='message mine'>
|
<div className='message mine'>
|
||||||
<p>General Emoji</p>
|
<p >General Emoji</p >
|
||||||
</div>
|
<div className='arrow-down' />
|
||||||
|
</div >
|
||||||
<div className='message theirs'>
|
<div className='message theirs'>
|
||||||
<p>we just achieved comedy</p>
|
<p >we just achieved comedy</p >
|
||||||
</div>
|
<div className='arrow-down' />
|
||||||
</div>
|
</div >
|
||||||
|
</div >
|
||||||
<div className='conversation_input'>
|
<div className='conversation_input'>
|
||||||
{/*<form action="#" onSubmit={submitCompose()}>*/}
|
{/*<form action="#" onSubmit={submitCompose()}>*/}
|
||||||
|
|
||||||
{/* <input type='text' name='compose'/>*/}
|
{/* <input type='text' name='compose'/>*/}
|
||||||
{/* <input type='submit' name='submit'/>*/}
|
{/* <input type='submit' name='submit'/>*/}
|
||||||
{/*</form>*/}
|
{/*</form>*/}
|
||||||
<ComposeFormContainer singleColumn/>
|
<ComposeFormContainer singleColumn />
|
||||||
</div>
|
</div >
|
||||||
{/*<ConversationSmall></ConversationSmall>*/}
|
{/*<ConversationSmall></ConversationSmall>*/}
|
||||||
</li>
|
</li >
|
||||||
</ul>
|
</ul >
|
||||||
</div>
|
</div >
|
||||||
</div>
|
</div >
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withRouter(NavigationPanel);
|
export default withRouter(NavigationPanel);
|
||||||
|
|
|
@ -853,11 +853,19 @@
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
|
text-align: right;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active {
|
&:active {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
|
||||||
|
margin-left: 1ch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status__content__spoiler-link {
|
.status__content__spoiler-link {
|
||||||
|
@ -875,6 +883,13 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status__wrapper {
|
||||||
|
&:hover {
|
||||||
|
background: mix($ui-base-lighter-color, $ui-base-color);
|
||||||
|
animation: background ease 0.5s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.status__wrapper--filtered {
|
.status__wrapper--filtered {
|
||||||
color: $dark-text-color;
|
color: $dark-text-color;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
|
|
@ -137,7 +137,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.getting-started__footer {
|
.getting-started__footer {
|
||||||
text-align: justify;
|
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
|
|
|
@ -30,6 +30,10 @@ $messagingBoxHeight: 20em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.conversation_created-at {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.conversation_stream {
|
.conversation_stream {
|
||||||
padding-top: 1em;
|
padding-top: 1em;
|
||||||
height: $messagingBoxHeight;
|
height: $messagingBoxHeight;
|
||||||
|
@ -42,16 +46,39 @@ $messagingBoxHeight: 20em;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mine {
|
.mine {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
background: $classic-primary-color;
|
background: $classic-primary-color;
|
||||||
|
float: right;
|
||||||
|
|
||||||
|
.arrow-down {
|
||||||
|
border-top-color: $classic-primary-color;
|
||||||
|
left: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.theirs {
|
.theirs {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: $ui-highlight-color;
|
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
|
- else
|
||||||
= table_link_to 'circle', t('admin.accounts.web'), web_path("accounts/#{account.id}")
|
= 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)
|
= 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_ip')
|
||||||
%th= t('admin.accounts.most_recent_activity')
|
%th= t('admin.accounts.most_recent_activity')
|
||||||
%th links
|
%th links
|
||||||
%th sign in
|
|
||||||
%th statuses
|
%th statuses
|
||||||
%th following
|
%th following
|
||||||
%th followers
|
%th followers
|
||||||
|
%th nuke
|
||||||
%tbody
|
%tbody
|
||||||
= render @accounts
|
= render @accounts
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue