Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
ce1c652623
55 changed files with 292 additions and 304 deletions
12
Gemfile.lock
12
Gemfile.lock
|
@ -154,6 +154,7 @@ GEM
|
||||||
net-http-persistent (~> 4.0)
|
net-http-persistent (~> 4.0)
|
||||||
nokogiri (~> 1, >= 1.10.8)
|
nokogiri (~> 1, >= 1.10.8)
|
||||||
base64 (0.1.1)
|
base64 (0.1.1)
|
||||||
|
bcp47_spec (0.2.1)
|
||||||
bcrypt (3.1.19)
|
bcrypt (3.1.19)
|
||||||
better_errors (2.10.1)
|
better_errors (2.10.1)
|
||||||
erubi (>= 1.0.0)
|
erubi (>= 1.0.0)
|
||||||
|
@ -375,19 +376,19 @@ GEM
|
||||||
reline (>= 0.3.8)
|
reline (>= 0.3.8)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.6.3)
|
json (2.6.3)
|
||||||
json-canonicalization (0.3.2)
|
json-canonicalization (1.0.0)
|
||||||
json-jwt (1.15.3)
|
json-jwt (1.15.3)
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
aes_key_wrap
|
aes_key_wrap
|
||||||
bindata
|
bindata
|
||||||
httpclient
|
httpclient
|
||||||
json-ld (3.2.5)
|
json-ld (3.3.1)
|
||||||
htmlentities (~> 4.3)
|
htmlentities (~> 4.3)
|
||||||
json-canonicalization (~> 0.3, >= 0.3.2)
|
json-canonicalization (~> 1.0)
|
||||||
link_header (~> 0.0, >= 0.0.8)
|
link_header (~> 0.0, >= 0.0.8)
|
||||||
multi_json (~> 1.15)
|
multi_json (~> 1.15)
|
||||||
rack (>= 2.2, < 4)
|
rack (>= 2.2, < 4)
|
||||||
rdf (~> 3.2, >= 3.2.10)
|
rdf (~> 3.3)
|
||||||
json-ld-preloaded (3.2.2)
|
json-ld-preloaded (3.2.2)
|
||||||
json-ld (~> 3.2)
|
json-ld (~> 3.2)
|
||||||
rdf (~> 3.2)
|
rdf (~> 3.2)
|
||||||
|
@ -596,7 +597,8 @@ GEM
|
||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
rake (13.0.6)
|
rake (13.0.6)
|
||||||
rdf (3.2.11)
|
rdf (3.3.1)
|
||||||
|
bcp47_spec (~> 0.2)
|
||||||
link_header (~> 0.0, >= 0.0.8)
|
link_header (~> 0.0, >= 0.0.8)
|
||||||
rdf-normalize (0.6.1)
|
rdf-normalize (0.6.1)
|
||||||
rdf (~> 3.2)
|
rdf (~> 3.2)
|
||||||
|
|
|
@ -17,7 +17,6 @@ import { Avatar } from './avatar';
|
||||||
import { Button } from './button';
|
import { Button } from './button';
|
||||||
import { FollowersCounter } from './counters';
|
import { FollowersCounter } from './counters';
|
||||||
import { DisplayName } from './display_name';
|
import { DisplayName } from './display_name';
|
||||||
import { IconButton } from './icon_button';
|
|
||||||
import Permalink from './permalink';
|
import Permalink from './permalink';
|
||||||
import { RelativeTimestamp } from './relative_timestamp';
|
import { RelativeTimestamp } from './relative_timestamp';
|
||||||
|
|
||||||
|
@ -45,10 +44,7 @@ class Account extends ImmutablePureComponent {
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
hidden: PropTypes.bool,
|
hidden: PropTypes.bool,
|
||||||
minimal: PropTypes.bool,
|
minimal: PropTypes.bool,
|
||||||
actionIcon: PropTypes.string,
|
|
||||||
actionTitle: PropTypes.string,
|
|
||||||
defaultAction: PropTypes.string,
|
defaultAction: PropTypes.string,
|
||||||
onActionClick: PropTypes.func,
|
|
||||||
withBio: PropTypes.bool,
|
withBio: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -76,12 +72,8 @@ class Account extends ImmutablePureComponent {
|
||||||
this.props.onMuteNotifications(this.props.account, false);
|
this.props.onMuteNotifications(this.props.account, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleAction = () => {
|
|
||||||
this.props.onActionClick(this.props.account);
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, intl, hidden, withBio, onActionClick, actionIcon, actionTitle, defaultAction, size, minimal } = this.props;
|
const { account, intl, hidden, withBio, defaultAction, size, minimal } = this.props;
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return <EmptyAccount size={size} minimal={minimal} />;
|
return <EmptyAccount size={size} minimal={minimal} />;
|
||||||
|
@ -98,9 +90,7 @@ class Account extends ImmutablePureComponent {
|
||||||
|
|
||||||
let buttons;
|
let buttons;
|
||||||
|
|
||||||
if (actionIcon && onActionClick) {
|
if (account.get('id') !== me && account.get('relationship', null) !== null) {
|
||||||
buttons = <IconButton icon={actionIcon} title={actionTitle} onClick={this.handleAction} />;
|
|
||||||
} else if (!actionIcon && account.get('id') !== me && account.get('relationship', null) !== null) {
|
|
||||||
const following = account.getIn(['relationship', 'following']);
|
const following = account.getIn(['relationship', 'following']);
|
||||||
const requested = account.getIn(['relationship', 'requested']);
|
const requested = account.getIn(['relationship', 'requested']);
|
||||||
const blocking = account.getIn(['relationship', 'blocking']);
|
const blocking = account.getIn(['relationship', 'blocking']);
|
||||||
|
|
|
@ -5,49 +5,44 @@ import { autoPlayGif } from '../initial_state';
|
||||||
import type { Account } from '../types/resources';
|
import type { Account } from '../types/resources';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
account: Account | undefined;
|
account: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there
|
||||||
className?: string;
|
|
||||||
size: number;
|
size: number;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
|
animate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Avatar: React.FC<Props> = ({
|
export const Avatar: React.FC<Props> = ({
|
||||||
account,
|
account,
|
||||||
className,
|
animate = autoPlayGif,
|
||||||
size = 20,
|
size = 20,
|
||||||
inline = false,
|
inline = false,
|
||||||
style: styleFromParent,
|
style: styleFromParent,
|
||||||
}) => {
|
}) => {
|
||||||
const { hovering, handleMouseEnter, handleMouseLeave } =
|
const { hovering, handleMouseEnter, handleMouseLeave } = useHovering(animate);
|
||||||
useHovering(autoPlayGif);
|
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
...styleFromParent,
|
...styleFromParent,
|
||||||
width: `${size}px`,
|
width: `${size}px`,
|
||||||
height: `${size}px`,
|
height: `${size}px`,
|
||||||
backgroundSize: `${size}px ${size}px`,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (account) {
|
const src =
|
||||||
style.backgroundImage = `url(${account.get(
|
hovering || animate
|
||||||
hovering ? 'avatar' : 'avatar_static',
|
? account?.get('avatar')
|
||||||
)})`;
|
: account?.get('avatar_static');
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames('account__avatar', {
|
||||||
'account__avatar',
|
'account__avatar-inline': inline,
|
||||||
{ 'account__avatar-inline': inline },
|
})}
|
||||||
className,
|
|
||||||
)}
|
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
style={style}
|
style={style}
|
||||||
data-avatar-of={account && `@${account.get('acct')}`}
|
data-avatar-of={account && `@${account.get('acct')}`}
|
||||||
role='img'
|
>
|
||||||
aria-label={account?.get('acct')}
|
{src && <img src={src} alt={account?.get('acct')} />}
|
||||||
/>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import { autoPlayGif } from '../initial_state';
|
import { autoPlayGif } from '../initial_state';
|
||||||
|
|
||||||
|
import { Avatar } from './avatar';
|
||||||
|
|
||||||
export default class AvatarComposite extends PureComponent {
|
export default class AvatarComposite extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -76,12 +78,12 @@ export default class AvatarComposite extends PureComponent {
|
||||||
bottom: bottom,
|
bottom: bottom,
|
||||||
width: `${width}%`,
|
width: `${width}%`,
|
||||||
height: `${height}%`,
|
height: `${height}%`,
|
||||||
backgroundSize: 'cover',
|
|
||||||
backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={account.get('id')} style={style} data-avatar-of={`@${account.get('acct')}`} />
|
<div key={account.get('id')} style={style}>
|
||||||
|
<Avatar account={account} animate={animate} />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { PureComponent } from 'react';
|
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
|
|
||||||
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
|
||||||
|
|
||||||
export default class AvatarOverlay extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
|
||||||
friend: ImmutablePropTypes.map.isRequired,
|
|
||||||
animate: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
animate: autoPlayGif,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { account, friend, animate } = this.props;
|
|
||||||
|
|
||||||
const baseStyle = {
|
|
||||||
backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`,
|
|
||||||
};
|
|
||||||
|
|
||||||
const overlayStyle = {
|
|
||||||
backgroundImage: `url(${friend.get(animate ? 'avatar' : 'avatar_static')})`,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='account__avatar-overlay'>
|
|
||||||
<div className='account__avatar-overlay-base' style={baseStyle} data-avatar-of={`@${account.get('acct')}`} />
|
|
||||||
<div className='account__avatar-overlay-overlay' style={overlayStyle} data-avatar-of={`@${friend.get('acct')}`} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
56
app/javascript/flavours/glitch/components/avatar_overlay.tsx
Normal file
56
app/javascript/flavours/glitch/components/avatar_overlay.tsx
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { useHovering } from '../hooks/useHovering';
|
||||||
|
import { autoPlayGif } from '../initial_state';
|
||||||
|
import type { Account } from '../types/resources';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
account: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there
|
||||||
|
friend: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there
|
||||||
|
size?: number;
|
||||||
|
baseSize?: number;
|
||||||
|
overlaySize?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AvatarOverlay: React.FC<Props> = ({
|
||||||
|
account,
|
||||||
|
friend,
|
||||||
|
size = 46,
|
||||||
|
baseSize = 36,
|
||||||
|
overlaySize = 24,
|
||||||
|
}) => {
|
||||||
|
const { hovering, handleMouseEnter, handleMouseLeave } =
|
||||||
|
useHovering(autoPlayGif);
|
||||||
|
const accountSrc = hovering
|
||||||
|
? account?.get('avatar')
|
||||||
|
: account?.get('avatar_static');
|
||||||
|
const friendSrc = hovering
|
||||||
|
? friend?.get('avatar')
|
||||||
|
: friend?.get('avatar_static');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className='account__avatar-overlay'
|
||||||
|
style={{ width: size, height: size }}
|
||||||
|
onMouseEnter={handleMouseEnter}
|
||||||
|
onMouseLeave={handleMouseLeave}
|
||||||
|
>
|
||||||
|
<div className='account__avatar-overlay-base'>
|
||||||
|
<div
|
||||||
|
className='account__avatar'
|
||||||
|
style={{ width: `${baseSize}px`, height: `${baseSize}px` }}
|
||||||
|
data-avatar-of={`@${account?.get('acct')}`}
|
||||||
|
>
|
||||||
|
{accountSrc && <img src={accountSrc} alt={account?.get('acct')} />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='account__avatar-overlay-overlay'>
|
||||||
|
<div
|
||||||
|
className='account__avatar'
|
||||||
|
style={{ width: `${overlaySize}px`, height: `${overlaySize}px` }}
|
||||||
|
data-avatar-of={`@${friend?.get('acct')}`}
|
||||||
|
>
|
||||||
|
{friendSrc && <img src={friendSrc} alt={friend?.get('acct')} />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -12,7 +12,6 @@ export default class Column extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
extraClasses: PropTypes.string,
|
extraClasses: PropTypes.string,
|
||||||
name: PropTypes.string,
|
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
bindToDocument: PropTypes.bool,
|
bindToDocument: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
@ -62,10 +61,10 @@ export default class Column extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { children, extraClasses, name, label } = this.props;
|
const { label, children, extraClasses } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div role='region' aria-label={label} data-column={name} className={`column ${extraClasses || ''}`} ref={this.setRef}>
|
<div role='region' aria-label={label} className={`column ${extraClasses || ''}`} ref={this.setRef}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -84,6 +84,7 @@ class Status extends ImmutablePureComponent {
|
||||||
previousId: PropTypes.string,
|
previousId: PropTypes.string,
|
||||||
nextInReplyToId: PropTypes.string,
|
nextInReplyToId: PropTypes.string,
|
||||||
rootId: PropTypes.string,
|
rootId: PropTypes.string,
|
||||||
|
onClick: PropTypes.func,
|
||||||
onReply: PropTypes.func,
|
onReply: PropTypes.func,
|
||||||
onFavourite: PropTypes.func,
|
onFavourite: PropTypes.func,
|
||||||
onReblog: PropTypes.func,
|
onReblog: PropTypes.func,
|
||||||
|
@ -116,7 +117,6 @@ class Status extends ImmutablePureComponent {
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
cacheMediaWidth: PropTypes.func,
|
cacheMediaWidth: PropTypes.func,
|
||||||
cachedMediaWidth: PropTypes.number,
|
cachedMediaWidth: PropTypes.number,
|
||||||
onClick: PropTypes.func,
|
|
||||||
scrollKey: PropTypes.string,
|
scrollKey: PropTypes.string,
|
||||||
deployPictureInPicture: PropTypes.func,
|
deployPictureInPicture: PropTypes.func,
|
||||||
settings: ImmutablePropTypes.map.isRequired,
|
settings: ImmutablePropTypes.map.isRequired,
|
||||||
|
@ -576,7 +576,7 @@ class Status extends ImmutablePureComponent {
|
||||||
openProfile: this.handleHotkeyOpenProfile,
|
openProfile: this.handleHotkeyOpenProfile,
|
||||||
moveUp: this.handleHotkeyMoveUp,
|
moveUp: this.handleHotkeyMoveUp,
|
||||||
moveDown: this.handleHotkeyMoveDown,
|
moveDown: this.handleHotkeyMoveDown,
|
||||||
toggleSpoiler: this.handleExpandedToggle,
|
toggleHidden: this.handleExpandedToggle,
|
||||||
bookmark: this.handleHotkeyBookmark,
|
bookmark: this.handleHotkeyBookmark,
|
||||||
toggleCollapse: this.handleHotkeyCollapse,
|
toggleCollapse: this.handleHotkeyCollapse,
|
||||||
toggleSensitive: this.handleHotkeyToggleSensitive,
|
toggleSensitive: this.handleHotkeyToggleSensitive,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
// Mastodon imports.
|
// Mastodon imports.
|
||||||
import { Avatar } from './avatar';
|
import { Avatar } from './avatar';
|
||||||
import AvatarOverlay from './avatar_overlay';
|
import { AvatarOverlay } from './avatar_overlay';
|
||||||
import { DisplayName } from './display_name';
|
import { DisplayName } from './display_name';
|
||||||
|
|
||||||
export default class StatusHeader extends PureComponent {
|
export default class StatusHeader extends PureComponent {
|
||||||
|
@ -39,7 +39,7 @@ export default class StatusHeader extends PureComponent {
|
||||||
|
|
||||||
let statusAvatar;
|
let statusAvatar;
|
||||||
if (friend === undefined || friend === null) {
|
if (friend === undefined || friend === null) {
|
||||||
statusAvatar = <Avatar account={account} size={48} />;
|
statusAvatar = <Avatar account={account} size={46} />;
|
||||||
} else {
|
} else {
|
||||||
statusAvatar = <AvatarOverlay account={account} friend={friend} />;
|
statusAvatar = <AvatarOverlay account={account} friend={friend} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { store } from 'flavours/glitch/store';
|
||||||
const title = process.env.NODE_ENV === 'production' ? siteTitle : `${siteTitle} (Dev)`;
|
const title = process.env.NODE_ENV === 'production' ? siteTitle : `${siteTitle} (Dev)`;
|
||||||
|
|
||||||
const hydrateAction = hydrateStore(initialState);
|
const hydrateAction = hydrateStore(initialState);
|
||||||
|
|
||||||
store.dispatch(hydrateAction);
|
store.dispatch(hydrateAction);
|
||||||
|
|
||||||
// check for deprecated local settings
|
// check for deprecated local settings
|
||||||
|
@ -71,8 +72,8 @@ export default class Mastodon extends PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldUpdateScroll (_, { location }) {
|
shouldUpdateScroll (prevRouterProps, { location }) {
|
||||||
return !(location.state?.mastodonModalKey);
|
return !(location.state?.mastodonModalKey && location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
|
|
@ -38,7 +38,7 @@ class FeaturedTags extends ImmutablePureComponent {
|
||||||
name={featuredTag.get('name')}
|
name={featuredTag.get('name')}
|
||||||
href={featuredTag.get('url')}
|
href={featuredTag.get('url')}
|
||||||
to={`/@${account.get('acct')}/tagged/${featuredTag.get('name')}`}
|
to={`/@${account.get('acct')}/tagged/${featuredTag.get('name')}`}
|
||||||
uses={featuredTag.get('statuses_count')}
|
uses={featuredTag.get('statuses_count') * 1}
|
||||||
withGraph={false}
|
withGraph={false}
|
||||||
description={((featuredTag.get('statuses_count') * 1) > 0) ? intl.formatMessage(messages.lastStatusAt, { date: intl.formatDate(featuredTag.get('last_status_at'), { month: 'short', day: '2-digit' }) }) : intl.formatMessage(messages.empty)}
|
description={((featuredTag.get('statuses_count') * 1) > 0) ? intl.formatMessage(messages.lastStatusAt, { date: intl.formatDate(featuredTag.get('last_status_at'), { month: 'short', day: '2-digit' }) }) : intl.formatMessage(messages.empty)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||||
import { Button } from 'flavours/glitch/components/button';
|
import { Button } from 'flavours/glitch/components/button';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
|
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
|
||||||
import { autoPlayGif, me, domain } from 'flavours/glitch/initial_state';
|
import { autoPlayGif, me, domain } from 'flavours/glitch/initial_state';
|
||||||
|
|
|
@ -76,7 +76,7 @@ export default class MediaItem extends ImmutablePureComponent {
|
||||||
if (['audio', 'video'].includes(attachment.get('type'))) {
|
if (['audio', 'video'].includes(attachment.get('type'))) {
|
||||||
content = (
|
content = (
|
||||||
<img
|
<img
|
||||||
src={attachment.get('preview_url') || attachment.getIn(['account', 'avatar_static'])}
|
src={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
|
||||||
alt={attachment.get('description')}
|
alt={attachment.get('description')}
|
||||||
lang={status.get('language')}
|
lang={status.get('language')}
|
||||||
onLoad={this.handleImageLoad}
|
onLoad={this.handleImageLoad}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||||
|
|
||||||
import AvatarOverlay from '../../../components/avatar_overlay';
|
import { AvatarOverlay } from '../../../components/avatar_overlay';
|
||||||
import { DisplayName } from '../../../components/display_name';
|
import { DisplayName } from '../../../components/display_name';
|
||||||
|
|
||||||
class MovedNote extends ImmutablePureComponent {
|
class MovedNote extends ImmutablePureComponent {
|
||||||
|
|
|
@ -184,7 +184,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
const remoteMessage = remote ? <RemoteHint url={remoteUrl} /> : null;
|
const remoteMessage = remote ? <RemoteHint url={remoteUrl} /> : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column ref={this.setRef} name='account'>
|
<Column ref={this.setRef}>
|
||||||
<ProfileColumnHeader onClick={this.handleHeaderClick} multiColumn={multiColumn} />
|
<ProfileColumnHeader onClick={this.handleHeaderClick} multiColumn={multiColumn} />
|
||||||
|
|
||||||
<StatusList
|
<StatusList
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { is } from 'immutable';
|
||||||
|
|
||||||
import { throttle, debounce } from 'lodash';
|
import { throttle, debounce } from 'lodash';
|
||||||
|
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { formatTime, getPointerPosition, fileNameFromURL } from 'flavours/glitch/features/video';
|
import { formatTime, getPointerPosition, fileNameFromURL } from 'flavours/glitch/features/video';
|
||||||
|
|
||||||
import { Blurhash } from '../../components/blurhash';
|
import { Blurhash } from '../../components/blurhash';
|
||||||
|
|
|
@ -59,7 +59,7 @@ class Blocks extends ImmutablePureComponent {
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />;
|
const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column name='blocks' bindToDocument={!multiColumn} icon='ban' heading={intl.formatMessage(messages.heading)}>
|
<Column bindToDocument={!multiColumn} icon='ban' heading={intl.formatMessage(messages.heading)}>
|
||||||
<ColumnBackButtonSlim />
|
<ColumnBackButtonSlim />
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='blocks'
|
scrollKey='blocks'
|
||||||
|
|
|
@ -77,7 +77,7 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.bookmarked_statuses' defaultMessage="You don't have any bookmarked posts yet. When you bookmark one, it will show up here." />;
|
const emptyMessage = <FormattedMessage id='empty_column.bookmarked_statuses' defaultMessage="You don't have any bookmarked posts yet. When you bookmark one, it will show up here." />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} ref={this.setRef} name='bookmarks'>
|
<Column bindToDocument={!multiColumn} ref={this.setRef}>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
icon='bookmark'
|
icon='bookmark'
|
||||||
title={intl.formatMessage(messages.heading)}
|
title={intl.formatMessage(messages.heading)}
|
||||||
|
|
|
@ -40,14 +40,14 @@ const mapStateToProps = (state, { columnId }) => {
|
||||||
|
|
||||||
class CommunityTimeline extends PureComponent {
|
class CommunityTimeline extends PureComponent {
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
onlyMedia: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
identity: PropTypes.object,
|
identity: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
onlyMedia: false,
|
||||||
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
|
@ -128,7 +128,7 @@ class CommunityTimeline extends PureComponent {
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column ref={this.setRef} name='local' bindToDocument={!multiColumn} label={intl.formatMessage(messages.title)}>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
icon='users'
|
icon='users'
|
||||||
active={hasUnread}
|
active={hasUnread}
|
||||||
|
|
|
@ -17,19 +17,21 @@ export default class NavigationBar extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
account: ImmutablePropTypes.map.isRequired,
|
||||||
onLogout: PropTypes.func.isRequired,
|
onLogout: PropTypes.func.isRequired,
|
||||||
|
onClose: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const username = this.props.account.get('acct');
|
||||||
return (
|
return (
|
||||||
<div className='navigation-bar'>
|
<div className='navigation-bar'>
|
||||||
<Permalink className='avatar' href={this.props.account.get('url')} to={`/@${this.props.account.get('acct')}`}>
|
<Permalink className='avatar' href={this.props.account.get('url')} to={`/@${username}`}>
|
||||||
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
|
<span style={{ display: 'none' }}>{username}</span>
|
||||||
<Avatar account={this.props.account} size={48} />
|
<Avatar account={this.props.account} size={46} />
|
||||||
</Permalink>
|
</Permalink>
|
||||||
|
|
||||||
<div className='navigation-bar__profile'>
|
<div className='navigation-bar__profile'>
|
||||||
<Permalink className='acct' href={this.props.account.get('url')} to={`/@${this.props.account.get('acct')}`}>
|
<Permalink className='acct' href={this.props.account.get('url')} to={`/@${this.props.account.get('acct')}`}>
|
||||||
<strong>@{this.props.account.get('acct')}</strong>
|
<strong className='navigation-bar__profile-account'>@{username}</strong>
|
||||||
</Permalink>
|
</Permalink>
|
||||||
|
|
||||||
{ profileLink !== undefined && (
|
{ profileLink !== undefined && (
|
||||||
|
|
|
@ -92,25 +92,6 @@ class Search extends PureComponent {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleBlur = () => {
|
|
||||||
this.setState({ expanded: false, selectedOption: -1 });
|
|
||||||
};
|
|
||||||
|
|
||||||
handleFocus = () => {
|
|
||||||
const { onShow, singleColumn } = this.props;
|
|
||||||
|
|
||||||
this.setState({ expanded: true, selectedOption: -1 });
|
|
||||||
onShow();
|
|
||||||
|
|
||||||
if (this.searchForm && !singleColumn) {
|
|
||||||
const { left, right } = this.searchForm.getBoundingClientRect();
|
|
||||||
|
|
||||||
if (left < 0 || right > (window.innerWidth || document.documentElement.clientWidth)) {
|
|
||||||
this.searchForm.scrollIntoView();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleKeyDown = (e) => {
|
handleKeyDown = (e) => {
|
||||||
const { selectedOption } = this.state;
|
const { selectedOption } = this.state;
|
||||||
const options = searchEnabled ? this._getOptions().concat(this.defaultOptions) : this._getOptions();
|
const options = searchEnabled ? this._getOptions().concat(this.defaultOptions) : this._getOptions();
|
||||||
|
@ -161,8 +142,23 @@ class Search extends PureComponent {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
findTarget = () => {
|
handleFocus = () => {
|
||||||
return this.searchForm;
|
const { onShow, singleColumn } = this.props;
|
||||||
|
|
||||||
|
this.setState({ expanded: true, selectedOption: -1 });
|
||||||
|
onShow();
|
||||||
|
|
||||||
|
if (this.searchForm && !singleColumn) {
|
||||||
|
const { left, right } = this.searchForm.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (left < 0 || right > (window.innerWidth || document.documentElement.clientWidth)) {
|
||||||
|
this.searchForm.scrollIntoView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleBlur = () => {
|
||||||
|
this.setState({ expanded: false, selectedOption: -1 });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleHashtagClick = () => {
|
handleHashtagClick = () => {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { LoadMore } from 'flavours/glitch/components/load_more';
|
import { LoadMore } from 'flavours/glitch/components/load_more';
|
||||||
import { SearchSection } from 'flavours/glitch/features/explore/components/search_section';
|
import { SearchSection } from 'flavours/glitch/features/explore/components/search_section';
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@ class SearchResults extends ImmutablePureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='drawer--results'>
|
<div className='drawer--results'>
|
||||||
<header className='search-results__header'>
|
<header className='search-results__header'>
|
||||||
|
|
|
@ -77,7 +77,7 @@ class Favourites extends ImmutablePureComponent {
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favorite posts yet. When you favorite one, it will show up here." />;
|
const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favorite posts yet. When you favorite one, it will show up here." />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} ref={this.setRef} name='favourites' label={intl.formatMessage(messages.heading)}>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
icon='star'
|
icon='star'
|
||||||
title={intl.formatMessage(messages.heading)}
|
title={intl.formatMessage(messages.heading)}
|
||||||
|
|
|
@ -85,9 +85,10 @@ class Favourites extends ImmutablePureComponent {
|
||||||
showBackButton
|
showBackButton
|
||||||
multiColumn={multiColumn}
|
multiColumn={multiColumn}
|
||||||
extraButton={(
|
extraButton={(
|
||||||
<button className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' /></button>
|
<button type='button' className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' /></button>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='favourites'
|
scrollKey='favourites'
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
|
|
|
@ -67,7 +67,7 @@ class FollowRequests extends ImmutablePureComponent {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} name='follow-requests' icon='user-plus' heading={intl.formatMessage(messages.heading)}>
|
<Column bindToDocument={!multiColumn} icon='user-plus' heading={intl.formatMessage(messages.heading)}>
|
||||||
<ColumnBackButtonSlim />
|
<ColumnBackButtonSlim />
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='follow_requests'
|
scrollKey='follow_requests'
|
||||||
|
|
|
@ -394,7 +394,7 @@ class Announcements extends ImmutablePureComponent {
|
||||||
_markAnnouncementAsRead () {
|
_markAnnouncementAsRead () {
|
||||||
const { dismissAnnouncement, announcements } = this.props;
|
const { dismissAnnouncement, announcements } = this.props;
|
||||||
const { index } = this.state;
|
const { index } = this.state;
|
||||||
const announcement = announcements.get(index);
|
const announcement = announcements.get(announcements.size - 1 - index);
|
||||||
if (!announcement.get('read')) dismissAnnouncement(announcement.get('id'));
|
if (!announcement.get('read')) dismissAnnouncement(announcement.get('id'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +435,7 @@ class Announcements extends ImmutablePureComponent {
|
||||||
selected={index === idx}
|
selected={index === idx}
|
||||||
disabled={disableSwiping}
|
disabled={disableSwiping}
|
||||||
/>
|
/>
|
||||||
))}
|
)).reverse()}
|
||||||
</ReactSwipeableViews>
|
</ReactSwipeableViews>
|
||||||
|
|
||||||
{announcements.size > 1 && (
|
{announcements.size > 1 && (
|
||||||
|
|
|
@ -173,7 +173,7 @@ class GettingStarted extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} name='getting-started' icon='asterisk' heading={intl.formatMessage(messages.heading)} label={intl.formatMessage(messages.menu)} hideHeadingOnMobile>
|
<Column bindToDocument={!multiColumn} icon='asterisk' heading={intl.formatMessage(messages.heading)} label={intl.formatMessage(messages.menu)} hideHeadingOnMobile>
|
||||||
<div className='scrollable optionally-scrollable'>
|
<div className='scrollable optionally-scrollable'>
|
||||||
<div className='getting-started__wrapper'>
|
<div className='getting-started__wrapper'>
|
||||||
{!multiColumn && signedIn && <NavigationBar account={myAccount} />}
|
{!multiColumn && signedIn && <NavigationBar account={myAccount} />}
|
||||||
|
|
|
@ -196,7 +196,7 @@ class HomeTimeline extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} ref={this.setRef} name='home' label={intl.formatMessage(messages.title)}>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
icon='home'
|
icon='home'
|
||||||
active={hasUnread}
|
active={hasUnread}
|
||||||
|
|
|
@ -16,7 +16,7 @@ const messages = defineMessages({
|
||||||
add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
|
add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = (state, { listId, added }) => ({
|
const MapStateToProps = (state, { listId, added }) => ({
|
||||||
list: state.get('lists').get(listId),
|
list: state.get('lists').get(listId),
|
||||||
added: typeof added === 'undefined' ? state.getIn(['listAdder', 'lists', 'items']).includes(listId) : added,
|
added: typeof added === 'undefined' ? state.getIn(['listAdder', 'lists', 'items']).includes(listId) : added,
|
||||||
});
|
});
|
||||||
|
@ -69,4 +69,4 @@ class List extends ImmutablePureComponent {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(List));
|
export default connect(MapStateToProps, mapDispatchToProps)(injectIntl(List));
|
||||||
|
|
|
@ -1,20 +1,39 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { removeFromListEditor, addToListEditor } from '../../../actions/lists';
|
||||||
import { Avatar } from '../../../components/avatar';
|
import { Avatar } from '../../../components/avatar';
|
||||||
import { DisplayName } from '../../../components/display_name';
|
import { DisplayName } from '../../../components/display_name';
|
||||||
import { IconButton } from '../../../components/icon_button';
|
import { IconButton } from '../../../components/icon_button';
|
||||||
|
import { makeGetAccount } from '../../../selectors';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
|
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
|
||||||
add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
|
add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class Account extends ImmutablePureComponent {
|
const makeMapStateToProps = () => {
|
||||||
|
const getAccount = makeGetAccount();
|
||||||
|
|
||||||
|
const mapStateToProps = (state, { accountId, added }) => ({
|
||||||
|
account: getAccount(state, accountId),
|
||||||
|
added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added,
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch, { accountId }) => ({
|
||||||
|
onRemove: () => dispatch(removeFromListEditor(accountId)),
|
||||||
|
onAdd: () => dispatch(addToListEditor(accountId)),
|
||||||
|
});
|
||||||
|
|
||||||
|
class Account extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
account: ImmutablePropTypes.map.isRequired,
|
||||||
|
@ -56,3 +75,5 @@ export default class Account extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(Account));
|
||||||
|
|
|
@ -1,17 +1,31 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
|
||||||
|
import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
search: { id: 'lists.search', defaultMessage: 'Search among people you follow' },
|
search: { id: 'lists.search', defaultMessage: 'Search among people you follow' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class Search extends PureComponent {
|
const mapStateToProps = state => ({
|
||||||
|
value: state.getIn(['listEditor', 'suggestions', 'value']),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
onSubmit: value => dispatch(fetchListSuggestions(value)),
|
||||||
|
onClear: () => dispatch(clearListSuggestions()),
|
||||||
|
onChange: value => dispatch(changeListSuggestions(value)),
|
||||||
|
});
|
||||||
|
|
||||||
|
class Search extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
|
@ -63,3 +77,5 @@ export default class Search extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(Search));
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { injectIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { removeFromListEditor, addToListEditor } from 'flavours/glitch/actions/lists';
|
|
||||||
import { makeGetAccount } from 'flavours/glitch/selectors';
|
|
||||||
|
|
||||||
import Account from '../components/account';
|
|
||||||
|
|
||||||
const makeMapStateToProps = () => {
|
|
||||||
const getAccount = makeGetAccount();
|
|
||||||
|
|
||||||
const mapStateToProps = (state, { accountId, added }) => ({
|
|
||||||
account: getAccount(state, accountId),
|
|
||||||
added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added,
|
|
||||||
});
|
|
||||||
|
|
||||||
return mapStateToProps;
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch, { accountId }) => ({
|
|
||||||
onRemove: () => dispatch(removeFromListEditor(accountId)),
|
|
||||||
onAdd: () => dispatch(addToListEditor(accountId)),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account));
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { injectIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists';
|
|
||||||
import Search from '../components/search';
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
value: state.getIn(['listEditor', 'suggestions', 'value']),
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
|
||||||
onSubmit: value => dispatch(fetchListSuggestions(value)),
|
|
||||||
onClear: () => dispatch(clearListSuggestions()),
|
|
||||||
onChange: value => dispatch(changeListSuggestions(value)),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Search));
|
|
|
@ -11,10 +11,9 @@ import spring from 'react-motion/lib/spring';
|
||||||
import { setupListEditor, clearListSuggestions, resetListEditor } from '../../actions/lists';
|
import { setupListEditor, clearListSuggestions, resetListEditor } from '../../actions/lists';
|
||||||
import Motion from '../ui/util/optional_motion';
|
import Motion from '../ui/util/optional_motion';
|
||||||
|
|
||||||
|
import Account from './components/account';
|
||||||
import EditListForm from './components/edit_list_form';
|
import EditListForm from './components/edit_list_form';
|
||||||
import AccountContainer from './containers/account_container';
|
import Search from './components/search';
|
||||||
import SearchContainer from './containers/search_container';
|
|
||||||
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
accountIds: state.getIn(['listEditor', 'accounts', 'items']),
|
accountIds: state.getIn(['listEditor', 'accounts', 'items']),
|
||||||
|
@ -58,21 +57,21 @@ class ListEditor extends ImmutablePureComponent {
|
||||||
<div className='modal-root__modal list-editor'>
|
<div className='modal-root__modal list-editor'>
|
||||||
<EditListForm />
|
<EditListForm />
|
||||||
|
|
||||||
<SearchContainer />
|
<Search />
|
||||||
|
|
||||||
<div className='drawer__pager'>
|
<div className='drawer__pager'>
|
||||||
<div className='drawer__inner list-editor__accounts'>
|
<div className='drawer__inner list-editor__accounts'>
|
||||||
{accountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} added />)}
|
{accountIds.map(accountId => <Account key={accountId} accountId={accountId} added />)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showSearch && <div role='button' tabIndex={-1} className='drawer__backdrop' onClick={onClear} />}
|
{showSearch && <div role='button' tabIndex={-1} className='drawer__backdrop' onClick={onClear} />}
|
||||||
|
|
||||||
<Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
|
<Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
|
||||||
{({ x }) =>
|
{({ x }) => (
|
||||||
(<div className='drawer__inner backdrop' style={{ transform: x === 0 ? null : `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
|
<div className='drawer__inner backdrop' style={{ transform: x === 0 ? null : `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
|
||||||
{searchAccountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} />)}
|
{searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)}
|
||||||
</div>)
|
</div>
|
||||||
}
|
)}
|
||||||
</Motion>
|
</Motion>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -61,7 +61,7 @@ class Mutes extends ImmutablePureComponent {
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />;
|
const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} name='mutes' icon='volume-off' heading={intl.formatMessage(messages.heading)}>
|
<Column bindToDocument={!multiColumn} icon='volume-off' heading={intl.formatMessage(messages.heading)}>
|
||||||
<ColumnBackButtonSlim />
|
<ColumnBackButtonSlim />
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='mutes'
|
scrollKey='mutes'
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Component } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
|
||||||
export default class ClearColumnButton extends Component {
|
export default class ClearColumnButton extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import AvatarOverlay from 'flavours/glitch/components/avatar_overlay';
|
import { AvatarOverlay } from 'flavours/glitch/components/avatar_overlay';
|
||||||
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
||||||
|
|
||||||
// This needs to be kept in sync with app/models/report.rb
|
// This needs to be kept in sync with app/models/report.rb
|
||||||
|
|
|
@ -333,7 +333,6 @@ class Notifications extends PureComponent {
|
||||||
<Column
|
<Column
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
ref={this.setColumnRef}
|
ref={this.setColumnRef}
|
||||||
name='notifications'
|
|
||||||
extraClasses={this.props.notifCleaningActive ? 'notif-cleaning' : null}
|
extraClasses={this.props.notifCleaningActive ? 'notif-cleaning' : null}
|
||||||
label={intl.formatMessage(messages.title)}
|
label={intl.formatMessage(messages.title)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -13,7 +13,7 @@ import SwipeableViews from 'react-swipeable-views';
|
||||||
|
|
||||||
import Column from 'flavours/glitch/components/column';
|
import Column from 'flavours/glitch/components/column';
|
||||||
import ColumnBackButton from 'flavours/glitch/components/column_back_button';
|
import ColumnBackButton from 'flavours/glitch/components/column_back_button';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { me, domain } from 'flavours/glitch/initial_state';
|
import { me, domain } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
import ArrowSmallRight from './components/arrow_small_right';
|
import ArrowSmallRight from './components/arrow_small_right';
|
||||||
|
|
|
@ -122,8 +122,8 @@ class Footer extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_performReblog = (privacy) => {
|
_performReblog = (status, privacy) => {
|
||||||
const { dispatch, status } = this.props;
|
const { dispatch } = this.props;
|
||||||
dispatch(reblog(status, privacy));
|
dispatch(reblog(status, privacy));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ class Footer extends ImmutablePureComponent {
|
||||||
if (status.get('reblogged')) {
|
if (status.get('reblogged')) {
|
||||||
dispatch(unreblog(status));
|
dispatch(unreblog(status));
|
||||||
} else if ((e && e.shiftKey) || !boostModal) {
|
} else if ((e && e.shiftKey) || !boostModal) {
|
||||||
this._performReblog();
|
this._performReblog(status);
|
||||||
} else {
|
} else {
|
||||||
dispatch(initBoostModal({ status, onReblog: this._performReblog }));
|
dispatch(initBoostModal({ status, onReblog: this._performReblog }));
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,7 @@ class PublicTimeline extends PureComponent {
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} ref={this.setRef} name='federated' label={intl.formatMessage(messages.title)}>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
icon='globe'
|
icon='globe'
|
||||||
active={hasUnread}
|
active={hasUnread}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import VisibilityIcon from 'flavours/glitch/components/status_visibility_icon';
|
||||||
|
|
||||||
import Option from './option';
|
import Option from './option';
|
||||||
|
|
||||||
export default class StatusCheckBox extends PureComponent {
|
class StatusCheckBox extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
|
@ -40,7 +40,9 @@ export default class StatusCheckBox extends PureComponent {
|
||||||
<Avatar account={status.get('account')} size={46} />
|
<Avatar account={status.get('account')} size={46} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div><DisplayName account={status.get('account')} /> · <VisibilityIcon visibility={status.get('visibility')} /><RelativeTimestamp timestamp={status.get('created_at')} /></div>
|
<div>
|
||||||
|
<DisplayName account={status.get('account')} /> · <VisibilityIcon visibility={status.get('visibility')} /><RelativeTimestamp timestamp={status.get('created_at')} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<StatusContent status={status} media={<MediaAttachments status={status} revealed={false} />} />
|
<StatusContent status={status} media={<MediaAttachments status={status} revealed={false} />} />
|
||||||
|
@ -61,3 +63,5 @@ export default class StatusCheckBox extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default StatusCheckBox;
|
||||||
|
|
|
@ -61,13 +61,13 @@ class ActionBar extends PureComponent {
|
||||||
onFavourite: PropTypes.func.isRequired,
|
onFavourite: PropTypes.func.isRequired,
|
||||||
onReactionAdd: PropTypes.func.isRequired,
|
onReactionAdd: PropTypes.func.isRequired,
|
||||||
onBookmark: PropTypes.func.isRequired,
|
onBookmark: PropTypes.func.isRequired,
|
||||||
onMute: PropTypes.func,
|
|
||||||
onMuteConversation: PropTypes.func,
|
|
||||||
onBlock: PropTypes.func,
|
|
||||||
onDelete: PropTypes.func.isRequired,
|
onDelete: PropTypes.func.isRequired,
|
||||||
onEdit: PropTypes.func.isRequired,
|
onEdit: PropTypes.func.isRequired,
|
||||||
onDirect: PropTypes.func.isRequired,
|
onDirect: PropTypes.func.isRequired,
|
||||||
onMention: PropTypes.func.isRequired,
|
onMention: PropTypes.func.isRequired,
|
||||||
|
onMute: PropTypes.func,
|
||||||
|
onBlock: PropTypes.func,
|
||||||
|
onMuteConversation: PropTypes.func,
|
||||||
onReport: PropTypes.func,
|
onReport: PropTypes.func,
|
||||||
onPin: PropTypes.func,
|
onPin: PropTypes.func,
|
||||||
onEmbed: PropTypes.func,
|
onEmbed: PropTypes.func,
|
||||||
|
@ -119,14 +119,14 @@ class ActionBar extends PureComponent {
|
||||||
this.props.onMute(this.props.status.get('account'));
|
this.props.onMute(this.props.status.get('account'));
|
||||||
};
|
};
|
||||||
|
|
||||||
handleConversationMuteClick = () => {
|
|
||||||
this.props.onMuteConversation(this.props.status);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleBlockClick = () => {
|
handleBlockClick = () => {
|
||||||
this.props.onBlock(this.props.status);
|
this.props.onBlock(this.props.status);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleConversationMuteClick = () => {
|
||||||
|
this.props.onMuteConversation(this.props.status);
|
||||||
|
};
|
||||||
|
|
||||||
handleReport = () => {
|
handleReport = () => {
|
||||||
this.props.onReport(this.props.status);
|
this.props.onReport(this.props.status);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,13 +3,13 @@ import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classnames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import Immutable from 'immutable';
|
import Immutable from 'immutable';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import { Blurhash } from 'flavours/glitch/components/blurhash';
|
import { Blurhash } from 'flavours/glitch/components/blurhash';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { useBlurhash } from 'flavours/glitch/initial_state';
|
import { useBlurhash } from 'flavours/glitch/initial_state';
|
||||||
import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
|
import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ export default class Card extends PureComponent {
|
||||||
const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name');
|
const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name');
|
||||||
const horizontal = (!compact && card.get('width') > card.get('height')) || card.get('type') !== 'link' || embedded;
|
const horizontal = (!compact && card.get('width') > card.get('height')) || card.get('type') !== 'link' || embedded;
|
||||||
const interactive = card.get('type') !== 'link';
|
const interactive = card.get('type') !== 'link';
|
||||||
const className = classnames('status-card', { horizontal, compact, interactive });
|
const className = classNames('status-card', { horizontal, compact, interactive });
|
||||||
const title = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener noreferrer' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
|
const title = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener noreferrer' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
|
||||||
const language = card.get('language') || '';
|
const language = card.get('language') || '';
|
||||||
|
|
||||||
|
@ -171,15 +171,17 @@ export default class Card extends PureComponent {
|
||||||
let embed = '';
|
let embed = '';
|
||||||
let canvas = (
|
let canvas = (
|
||||||
<Blurhash
|
<Blurhash
|
||||||
className={classnames('status-card__image-preview', {
|
className={classNames('status-card__image-preview', {
|
||||||
'status-card__image-preview--hidden': revealed && this.state.previewLoaded,
|
'status-card__image-preview--hidden': revealed && this.state.previewLoaded,
|
||||||
})}
|
})}
|
||||||
hash={card.get('blurhash')}
|
hash={card.get('blurhash')}
|
||||||
dummy={!useBlurhash}
|
dummy={!useBlurhash}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const thumbnailDescription = card.get('image_description');
|
const thumbnailDescription = card.get('image_description');
|
||||||
const thumbnail = <img src={card.get('image')} alt={thumbnailDescription} title={thumbnailDescription} lang={language} style={thumbnailStyle} onLoad={this.handleImageLoad} className='status-card__image-image' />;
|
const thumbnail = <img src={card.get('image')} alt={thumbnailDescription} title={thumbnailDescription} lang={language} style={thumbnailStyle} onLoad={this.handleImageLoad} className='status-card__image-image' />;
|
||||||
|
|
||||||
let spoilerButton = (
|
let spoilerButton = (
|
||||||
<button type='button' onClick={this.handleReveal} className='spoiler-button__overlay'>
|
<button type='button' onClick={this.handleReveal} className='spoiler-button__overlay'>
|
||||||
<span className='spoiler-button__overlay__label'>
|
<span className='spoiler-button__overlay__label'>
|
||||||
|
@ -188,8 +190,9 @@ export default class Card extends PureComponent {
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
||||||
spoilerButton = (
|
spoilerButton = (
|
||||||
<div className={classnames('spoiler-button', { 'spoiler-button--minified': revealed })}>
|
<div className={classNames('spoiler-button', { 'spoiler-button--minified': revealed })}>
|
||||||
{spoilerButton}
|
{spoilerButton}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -209,15 +212,14 @@ export default class Card extends PureComponent {
|
||||||
{canvas}
|
{canvas}
|
||||||
{thumbnail}
|
{thumbnail}
|
||||||
|
|
||||||
{revealed && (
|
{revealed ? (
|
||||||
<div className='status-card__actions'>
|
<div className='status-card__actions'>
|
||||||
<div>
|
<div>
|
||||||
<button onClick={this.handleEmbedClick}><Icon id={iconVariant} /></button>
|
<button type='button' onClick={this.handleEmbedClick}><Icon id={iconVariant} /></button>
|
||||||
{horizontal && <a href={card.get('url')} target='_blank' rel='noopener noreferrer'><Icon id='external-link' /></a>}
|
{horizontal && <a href={card.get('url')} target='_blank' rel='noopener noreferrer'><Icon id='external-link' /></a>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : spoilerButton}
|
||||||
{!revealed && spoilerButton}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { AnimatedNumber } from 'flavours/glitch/components/animated_number';
|
||||||
import AttachmentList from 'flavours/glitch/components/attachment_list';
|
import AttachmentList from 'flavours/glitch/components/attachment_list';
|
||||||
import EditedTimestamp from 'flavours/glitch/components/edited_timestamp';
|
import EditedTimestamp from 'flavours/glitch/components/edited_timestamp';
|
||||||
import { getHashtagBarForStatus } from 'flavours/glitch/components/hashtag_bar';
|
import { getHashtagBarForStatus } from 'flavours/glitch/components/hashtag_bar';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
|
import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
|
||||||
import VisibilityIcon from 'flavours/glitch/components/status_visibility_icon';
|
import VisibilityIcon from 'flavours/glitch/components/status_visibility_icon';
|
||||||
import PollContainer from 'flavours/glitch/containers/poll_container';
|
import PollContainer from 'flavours/glitch/containers/poll_container';
|
||||||
|
|
|
@ -726,7 +726,7 @@ class Status extends ImmutablePureComponent {
|
||||||
bookmark: this.handleHotkeyBookmark,
|
bookmark: this.handleHotkeyBookmark,
|
||||||
mention: this.handleHotkeyMention,
|
mention: this.handleHotkeyMention,
|
||||||
openProfile: this.handleHotkeyOpenProfile,
|
openProfile: this.handleHotkeyOpenProfile,
|
||||||
toggleSpoiler: this.handleToggleHidden,
|
toggleHidden: this.handleToggleHidden,
|
||||||
toggleSensitive: this.handleHotkeyToggleSensitive,
|
toggleSensitive: this.handleHotkeyToggleSensitive,
|
||||||
openMedia: this.handleHotkeyOpenMedia,
|
openMedia: this.handleHotkeyOpenMedia,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Component } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
const emptyComponent = () => null;
|
const emptyComponent = () => null;
|
||||||
const noop = () => { };
|
const noop = () => { };
|
||||||
|
|
||||||
class Bundle extends Component {
|
class Bundle extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
fetchComponent: PropTypes.func.isRequired,
|
fetchComponent: PropTypes.func.isRequired,
|
||||||
|
@ -26,7 +26,7 @@ class Bundle extends Component {
|
||||||
onFetchFail: noop,
|
onFetchFail: noop,
|
||||||
};
|
};
|
||||||
|
|
||||||
static cache = {};
|
static cache = new Map;
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
mod: undefined,
|
mod: undefined,
|
||||||
|
@ -51,6 +51,7 @@ class Bundle extends Component {
|
||||||
|
|
||||||
load = (props) => {
|
load = (props) => {
|
||||||
const { fetchComponent, onFetch, onFetchSuccess, onFetchFail, renderDelay } = props || this.props;
|
const { fetchComponent, onFetch, onFetchSuccess, onFetchFail, renderDelay } = props || this.props;
|
||||||
|
const cachedMod = Bundle.cache.get(fetchComponent);
|
||||||
|
|
||||||
if (fetchComponent === undefined) {
|
if (fetchComponent === undefined) {
|
||||||
this.setState({ mod: null });
|
this.setState({ mod: null });
|
||||||
|
@ -59,10 +60,8 @@ class Bundle extends Component {
|
||||||
|
|
||||||
onFetch();
|
onFetch();
|
||||||
|
|
||||||
if (Bundle.cache[fetchComponent.name]) {
|
if (cachedMod) {
|
||||||
const mod = Bundle.cache[fetchComponent.name];
|
this.setState({ mod: cachedMod.default });
|
||||||
|
|
||||||
this.setState({ mod: mod.default });
|
|
||||||
onFetchSuccess();
|
onFetchSuccess();
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -76,7 +75,7 @@ class Bundle extends Component {
|
||||||
|
|
||||||
return fetchComponent()
|
return fetchComponent()
|
||||||
.then((mod) => {
|
.then((mod) => {
|
||||||
Bundle.cache[fetchComponent.name] = mod;
|
Bundle.cache.set(fetchComponent, mod);
|
||||||
this.setState({ mod: mod.default });
|
this.setState({ mod: mod.default });
|
||||||
onFetchSuccess();
|
onFetchSuccess();
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Component } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ const messages = defineMessages({
|
||||||
close: { id: 'bundle_modal_error.close', defaultMessage: 'Close' },
|
close: { id: 'bundle_modal_error.close', defaultMessage: 'Close' },
|
||||||
});
|
});
|
||||||
|
|
||||||
class BundleModalError extends Component {
|
class BundleModalError extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onRetry: PropTypes.func.isRequired,
|
onRetry: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -75,7 +75,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
|
||||||
this.isRtlLayout = document.getElementsByTagName('body')[0].classList.contains('rtl');
|
this.isRtlLayout = document.getElementsByTagName('body')[0].classList.contains('rtl');
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUpdate(nextProps) {
|
UNSAFE_componentWillUpdate(nextProps) {
|
||||||
if (this.props.singleColumn !== nextProps.singleColumn && nextProps.singleColumn) {
|
if (this.props.singleColumn !== nextProps.singleColumn && nextProps.singleColumn) {
|
||||||
this.node.removeEventListener('wheel', this.handleWheel);
|
this.node.removeEventListener('wheel', this.handleWheel);
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,24 +96,10 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
window.addEventListener('keydown', this.handleKeyDown, false);
|
window.addEventListener('keydown', this.handleKeyDown, false);
|
||||||
|
|
||||||
this._sendBackgroundColor();
|
this._sendBackgroundColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
window.removeEventListener('keydown', this.handleKeyDown);
|
|
||||||
this.props.onChangeBackgroundColor(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
getIndex () {
|
|
||||||
return this.state.index !== null ? this.state.index : this.props.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleNavigation = () => {
|
|
||||||
this.setState(prevState => ({
|
|
||||||
navigationHidden: !prevState.navigationHidden,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidUpdate (prevProps, prevState) {
|
componentDidUpdate (prevProps, prevState) {
|
||||||
if (prevState.index !== this.state.index) {
|
if (prevState.index !== this.state.index) {
|
||||||
this._sendBackgroundColor();
|
this._sendBackgroundColor();
|
||||||
|
@ -131,6 +117,22 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
window.removeEventListener('keydown', this.handleKeyDown);
|
||||||
|
|
||||||
|
this.props.onChangeBackgroundColor(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndex () {
|
||||||
|
return this.state.index !== null ? this.state.index : this.props.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleNavigation = () => {
|
||||||
|
this.setState(prevState => ({
|
||||||
|
navigationHidden: !prevState.navigationHidden,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { media, statusId, lang, intl, onClose } = this.props;
|
const { media, statusId, lang, intl, onClose } = this.props;
|
||||||
const { navigationHidden } = this.state;
|
const { navigationHidden } = this.state;
|
||||||
|
|
|
@ -48,7 +48,7 @@ class NavigationPanel extends Component {
|
||||||
return match || location.pathname.startsWith('/public');
|
return match || location.pathname.startsWith('/public');
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render () {
|
||||||
const { intl, onOpenSettings } = this.props;
|
const { intl, onOpenSettings } = this.props;
|
||||||
const { signedIn, disabledAccountId } = this.context.identity;
|
const { signedIn, disabledAccountId } = this.context.identity;
|
||||||
|
|
||||||
|
|
|
@ -40,14 +40,14 @@ export default class UploadArea extends PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Motion defaultStyle={{ backgroundOpacity: 0, backgroundScale: 0.95 }} style={{ backgroundOpacity: spring(active ? 1 : 0, { stiffness: 150, damping: 15 }), backgroundScale: spring(active ? 1 : 0.95, { stiffness: 200, damping: 3 }) }}>
|
<Motion defaultStyle={{ backgroundOpacity: 0, backgroundScale: 0.95 }} style={{ backgroundOpacity: spring(active ? 1 : 0, { stiffness: 150, damping: 15 }), backgroundScale: spring(active ? 1 : 0.95, { stiffness: 200, damping: 3 }) }}>
|
||||||
{({ backgroundOpacity, backgroundScale }) =>
|
{({ backgroundOpacity, backgroundScale }) => (
|
||||||
(<div className='upload-area' style={{ visibility: active ? 'visible' : 'hidden', opacity: backgroundOpacity }}>
|
<div className='upload-area' style={{ visibility: active ? 'visible' : 'hidden', opacity: backgroundOpacity }}>
|
||||||
<div className='upload-area__drop'>
|
<div className='upload-area__drop'>
|
||||||
<div className='upload-area__background' style={{ transform: `scale(${backgroundScale})` }} />
|
<div className='upload-area__background' style={{ transform: `scale(${backgroundScale})` }} />
|
||||||
<div className='upload-area__content'><FormattedMessage id='upload_area.title' defaultMessage='Drag & drop to upload' /></div>
|
<div className='upload-area__content'><FormattedMessage id='upload_area.title' defaultMessage='Drag & drop to upload' /></div>
|
||||||
</div>
|
</div>
|
||||||
</div>)
|
</div>
|
||||||
}
|
)}
|
||||||
</Motion>
|
</Motion>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent, Component } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
|
@ -68,9 +68,10 @@ import {
|
||||||
PrivacyPolicy,
|
PrivacyPolicy,
|
||||||
} from './util/async-components';
|
} from './util/async-components';
|
||||||
import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers';
|
import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers';
|
||||||
|
|
||||||
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
||||||
// Without this it ends up in ~8 very commonly used bundles.
|
// Without this it ends up in ~8 very commonly used bundles.
|
||||||
import "../../components/status";
|
import '../../components/status';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' },
|
beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' },
|
||||||
|
@ -119,7 +120,7 @@ const keyMap = {
|
||||||
goToBlocked: 'g b',
|
goToBlocked: 'g b',
|
||||||
goToMuted: 'g m',
|
goToMuted: 'g m',
|
||||||
goToRequests: 'g r',
|
goToRequests: 'g r',
|
||||||
toggleSpoiler: 'x',
|
toggleHidden: 'x',
|
||||||
bookmark: 'd',
|
bookmark: 'd',
|
||||||
toggleCollapse: 'shift+x',
|
toggleCollapse: 'shift+x',
|
||||||
toggleSensitive: 'h',
|
toggleSensitive: 'h',
|
||||||
|
@ -255,7 +256,7 @@ class SwitchingColumnsArea extends PureComponent {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class UI extends Component {
|
class UI extends PureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
identity: PropTypes.object.isRequired,
|
identity: PropTypes.object.isRequired,
|
||||||
|
@ -270,7 +271,6 @@ class UI extends Component {
|
||||||
hasComposingText: PropTypes.bool,
|
hasComposingText: PropTypes.bool,
|
||||||
hasMediaAttachments: PropTypes.bool,
|
hasMediaAttachments: PropTypes.bool,
|
||||||
canUploadMore: PropTypes.bool,
|
canUploadMore: PropTypes.bool,
|
||||||
match: PropTypes.object.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
dropdownMenuIsOpen: PropTypes.bool,
|
dropdownMenuIsOpen: PropTypes.bool,
|
||||||
unreadNotifications: PropTypes.number,
|
unreadNotifications: PropTypes.number,
|
||||||
|
@ -287,7 +287,7 @@ class UI extends Component {
|
||||||
draggingOver: false,
|
draggingOver: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleBeforeUnload = (e) => {
|
handleBeforeUnload = e => {
|
||||||
const { intl, dispatch, hasComposingText, hasMediaAttachments } = this.props;
|
const { intl, dispatch, hasComposingText, hasMediaAttachments } = this.props;
|
||||||
|
|
||||||
dispatch(synchronouslySubmitMarkers());
|
dispatch(synchronouslySubmitMarkers());
|
||||||
|
@ -300,6 +300,14 @@ class UI extends Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleVisibilityChange = () => {
|
||||||
|
const visibility = !document[this.visibilityHiddenProp];
|
||||||
|
this.props.dispatch(notificationsSetVisibility(visibility));
|
||||||
|
if (visibility) {
|
||||||
|
this.props.dispatch(submitMarkers({ immediate: true }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
handleDragEnter = (e) => {
|
handleDragEnter = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
@ -311,13 +319,14 @@ class UI extends Component {
|
||||||
this.dragTargets.push(e.target);
|
this.dragTargets.push(e.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.dataTransfer && e.dataTransfer.types.includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) {
|
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) {
|
||||||
this.setState({ draggingOver: true });
|
this.setState({ draggingOver: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDragOver = (e) => {
|
handleDragOver = (e) => {
|
||||||
if (this.dataTransferIsText(e.dataTransfer)) return false;
|
if (this.dataTransferIsText(e.dataTransfer)) return false;
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
@ -372,14 +381,6 @@ class UI extends Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleVisibilityChange = () => {
|
|
||||||
const visibility = !document[this.visibilityHiddenProp];
|
|
||||||
this.props.dispatch(notificationsSetVisibility(visibility));
|
|
||||||
if (visibility) {
|
|
||||||
this.props.dispatch(submitMarkers({ immediate: true }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleLayoutChange = debounce(() => {
|
handleLayoutChange = debounce(() => {
|
||||||
this.props.dispatch(clearHeight()); // The cached heights are no longer accurate, invalidate
|
this.props.dispatch(clearHeight()); // The cached heights are no longer accurate, invalidate
|
||||||
}, 500, {
|
}, 500, {
|
||||||
|
|
|
@ -85,10 +85,14 @@
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
overflow: hidden;
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
img {
|
||||||
background-size: 36px 36px;
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
&-inline {
|
&-inline {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -102,7 +106,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
& div {
|
& > div {
|
||||||
@include avatar-radius;
|
@include avatar-radius;
|
||||||
|
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -110,6 +114,11 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.account__avatar {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
&__label {
|
&__label {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -125,37 +134,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.account__avatar-overlay {
|
.account__avatar-overlay {
|
||||||
@include avatar-size(48px);
|
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&-base {
|
|
||||||
@include avatar-radius;
|
|
||||||
@include avatar-size(36px);
|
|
||||||
|
|
||||||
img {
|
|
||||||
@include avatar-radius;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-overlay {
|
&-overlay {
|
||||||
@include avatar-radius;
|
|
||||||
@include avatar-size(24px);
|
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
inset-inline-end: 0;
|
inset-inline-end: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
img {
|
|
||||||
@include avatar-radius;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -701,8 +701,6 @@ a.status__display-name,
|
||||||
.status__avatar {
|
.status__avatar {
|
||||||
flex: none;
|
flex: none;
|
||||||
margin-inline-end: 10px;
|
margin-inline-end: 10px;
|
||||||
height: 48px;
|
|
||||||
width: 48px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.muted {
|
.muted {
|
||||||
|
@ -1068,7 +1066,7 @@ a.status-card.compact:hover {
|
||||||
width: 0;
|
width: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
inset-inline-start: 14px + ((48px - 2px) * 0.5);
|
inset-inline-start: 14px + ((46px - 2px) * 0.5);
|
||||||
|
|
||||||
&--full {
|
&--full {
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -1079,7 +1077,7 @@ a.status-card.compact:hover {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px - 4px;
|
top: 10px - 4px;
|
||||||
height: 48px + 4px + 4px;
|
height: 46px + 4px + 4px;
|
||||||
width: 2px;
|
width: 2px;
|
||||||
background: $ui-base-color;
|
background: $ui-base-color;
|
||||||
inset-inline-start: -2px;
|
inset-inline-start: -2px;
|
||||||
|
@ -1087,8 +1085,8 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
&--first {
|
&--first {
|
||||||
top: 10px + 48px + 4px;
|
top: 10px + 46px + 4px;
|
||||||
height: calc(100% - (10px + 48px + 4px));
|
height: calc(100% - (10px + 46px + 4px));
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
Loading…
Reference in a new issue