Merge pull request #1567 from ClearlyClaire/glitch-soc/merge-upstream

Merge upstream changes
main
Claire 3 years ago committed by GitHub
commit e42ed4502f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,4 @@
# CODEOWNERS for tootsuite/mastodon
# CODEOWNERS for mastodon/mastodon
# Translators
# To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address.

@ -1,7 +1,7 @@
Authors
=======
Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
Mastodon is available on [GitHub](https://github.com/mastodon/mastodon)
and provided thanks to the work of the following contributors:
* [Gargron](https://github.com/Gargron)
@ -719,7 +719,7 @@ and provided thanks to the work of the following contributors:
* [西小倉宏信](mailto:nishiko@mindia.jp)
* [雨宮美羽](mailto:k737566@gmail.com)
This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/tootsuite/mastodon/graphs/contributors) instead.
This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/mastodon/mastodon/graphs/contributors) instead.
## Translators

File diff suppressed because it is too large Load Diff

@ -48,7 +48,7 @@ If your contributions are accepted into Mastodon, you can request to be paid thr
## Bug reports
Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected.
Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/mastodon/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected.
## Translations
@ -78,6 +78,6 @@ It is not always possible to phrase every change in such a manner, but it is des
## Documentation
The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to tootsuite/documentation](https://github.com/tootsuite/documentation).
The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/documentation](https://github.com/mastodon/documentation).
</blockquote>

@ -3,8 +3,10 @@
> Now with automated deploys!
[![Build Status](https://img.shields.io/circleci/project/github/glitch-soc/mastodon.svg)][circleci]
[![Code Climate](https://img.shields.io/codeclimate/maintainability/glitch-soc/mastodon.svg)][code_climate]
[circleci]: https://circleci.com/gh/glitch-soc/mastodon
[code_climate]: https://codeclimate.com/github/glitch-soc/mastodon
So here's the deal: we all work on this code, and then it runs on dev.glitch.social and anyone who uses that does so absolutely at their own risk. can you dig it?

@ -1,8 +1,8 @@
{
"name": "Mastodon",
"description": "A GNU Social-compatible microblogging server",
"repository": "https://github.com/tootsuite/mastodon",
"logo": "https://github.com/tootsuite.png",
"repository": "https://github.com/mastodon/mastodon",
"logo": "https://github.com/mastodon.png",
"env": {
"HEROKU": {
"description": "Leave this as true",

@ -93,7 +93,7 @@ export default class IntersectionObserverArticle extends React.Component {
// When the browser gets a chance, test if we're still not intersecting,
// and if so, set our isHidden to true to trigger an unrender. The point of
// this is to save DOM nodes and avoid using up too much memory.
// See: https://github.com/tootsuite/mastodon/issues/2900
// See: https://github.com/mastodon/mastodon/issues/2900
this.setState((prevState) => ({ isHidden: !prevState.isIntersecting }));
}

@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import 'wicg-inert';
import { createBrowserHistory } from 'history';
import { multiply } from 'color-blend';
export default class ModalRoot extends React.PureComponent {
@ -48,6 +49,7 @@ export default class ModalRoot extends React.PureComponent {
componentDidMount () {
window.addEventListener('keyup', this.handleKeyUp, false);
window.addEventListener('keydown', this.handleKeyDown, false);
this.history = this.context.router ? this.context.router.history : createBrowserHistory();
}
componentWillReceiveProps (nextProps) {
@ -69,6 +71,14 @@ export default class ModalRoot extends React.PureComponent {
this.activeElement.focus({ preventScroll: true });
this.activeElement = null;
}).catch(console.error);
this._handleModalClose();
}
if (this.props.children && !prevProps.children) {
this._handleModalOpen();
}
if (this.props.children) {
this._ensureHistoryBuffer();
}
}
@ -77,6 +87,32 @@ export default class ModalRoot extends React.PureComponent {
window.removeEventListener('keydown', this.handleKeyDown);
}
_handleModalOpen () {
this._modalHistoryKey = Date.now();
this.unlistenHistory = this.history.listen((_, action) => {
if (action === 'POP') {
this.props.onClose();
}
});
}
_handleModalClose () {
if (this.unlistenHistory) {
this.unlistenHistory();
}
const { state } = this.history.location;
if (state && state.mastodonModalKey === this._modalHistoryKey) {
this.history.goBack();
}
}
_ensureHistoryBuffer () {
const { pathname, state } = this.history.location;
if (!state || state.mastodonModalKey !== this._modalHistoryKey) {
this.history.push(pathname, { ...state, mastodonModalKey: this._modalHistoryKey });
}
}
getSiblings = () => {
return Array(...this.node.parentElement.childNodes).filter(node => node !== this.node);
}

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react';
import { ScrollContainer } from 'react-router-scroll-4';
import ScrollContainer from 'mastodon/containers/scroll_container';
import PropTypes from 'prop-types';
import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container';
import LoadMore from './load_more';
@ -34,7 +34,6 @@ class ScrollableList extends PureComponent {
onScrollToTop: PropTypes.func,
onScroll: PropTypes.func,
trackScroll: PropTypes.bool,
shouldUpdateScroll: PropTypes.func,
isLoading: PropTypes.bool,
showLoading: PropTypes.bool,
hasMore: PropTypes.bool,
@ -290,7 +289,7 @@ class ScrollableList extends PureComponent {
}
render () {
const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props;
const { children, scrollKey, trackScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props;
const { fullscreen } = this.state;
const childrenCount = React.Children.count(children);
@ -356,7 +355,7 @@ class ScrollableList extends PureComponent {
if (trackScroll) {
return (
<ScrollContainer scrollKey={scrollKey} shouldUpdateScroll={shouldUpdateScroll}>
<ScrollContainer scrollKey={scrollKey}>
{scrollableArea}
</ScrollContainer>
);

@ -18,7 +18,6 @@ export default class StatusList extends ImmutablePureComponent {
onScrollToTop: PropTypes.func,
onScroll: PropTypes.func,
trackScroll: PropTypes.bool,
shouldUpdateScroll: PropTypes.func,
isLoading: PropTypes.bool,
isPartial: PropTypes.bool,
hasMore: PropTypes.bool,
@ -77,7 +76,7 @@ export default class StatusList extends ImmutablePureComponent {
}
render () {
const { statusIds, featuredStatusIds, shouldUpdateScroll, onLoadMore, timelineId, ...other } = this.props;
const { statusIds, featuredStatusIds, onLoadMore, timelineId, ...other } = this.props;
const { isLoading, isPartial } = other;
if (isPartial) {
@ -120,7 +119,7 @@ export default class StatusList extends ImmutablePureComponent {
}
return (
<ScrollableList {...other} showLoading={isLoading && statusIds.size === 0} onLoadMore={onLoadMore && this.handleLoadOlder} shouldUpdateScroll={shouldUpdateScroll} ref={this.setRef}>
<ScrollableList {...other} showLoading={isLoading && statusIds.size === 0} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
{scrollableContent}
</ScrollableList>
);

@ -10,8 +10,6 @@ import { hydrateStore } from '../actions/store';
import { connectUserStream } from '../actions/streaming';
import { IntlProvider, addLocaleData } from 'react-intl';
import { getLocale } from '../locales';
import { previewState as previewMediaState } from 'mastodon/features/ui/components/media_modal';
import { previewState as previewVideoState } from 'mastodon/features/ui/components/video_modal';
import initialState from '../initial_state';
import ErrorBoundary from '../components/error_boundary';
@ -41,8 +39,8 @@ export default class Mastodon extends React.PureComponent {
}
}
shouldUpdateScroll (_, { location }) {
return location.state !== previewMediaState && location.state !== previewVideoState;
shouldUpdateScroll (prevRouterProps, { location }) {
return !(location.state?.mastodonModalKey && location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey);
}
render () {

@ -0,0 +1,18 @@
import { ScrollContainer as OriginalScrollContainer } from 'react-router-scroll-4';
// ScrollContainer is used to automatically scroll to the top when pushing a
// new history state and remembering the scroll position when going back.
// There are a few things we need to do differently, though.
const defaultShouldUpdateScroll = (prevRouterProps, { location }) => {
// If the change is caused by opening a modal, do not scroll to top
return !(location.state?.mastodonModalKey && location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey);
};
export default
class ScrollContainer extends OriginalScrollContainer {
static defaultProps = {
shouldUpdateScroll: defaultShouldUpdateScroll,
};
}

@ -11,7 +11,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { getAccountGallery } from 'mastodon/selectors';
import MediaItem from './components/media_item';
import HeaderContainer from '../account_timeline/containers/header_container';
import { ScrollContainer } from 'react-router-scroll-4';
import ScrollContainer from 'mastodon/containers/scroll_container';
import LoadMore from 'mastodon/components/load_more';
import MissingIndicator from 'mastodon/components/missing_indicator';
import { openModal } from 'mastodon/actions/modal';
@ -29,7 +29,6 @@ const mapStateToProps = (state, props) => ({
class LoadMoreMedia extends ImmutablePureComponent {
static propTypes = {
shouldUpdateScroll: PropTypes.func,
maxId: PropTypes.string,
onLoadMore: PropTypes.func.isRequired,
};
@ -127,7 +126,7 @@ class AccountGallery extends ImmutablePureComponent {
}
render () {
const { attachments, shouldUpdateScroll, isLoading, hasMore, isAccount, multiColumn, blockedBy, suspended } = this.props;
const { attachments, isLoading, hasMore, isAccount, multiColumn, blockedBy, suspended } = this.props;
const { width } = this.state;
if (!isAccount) {
@ -164,7 +163,7 @@ class AccountGallery extends ImmutablePureComponent {
<Column>
<ColumnBackButton multiColumn={multiColumn} />
<ScrollContainer scrollKey='account_gallery' shouldUpdateScroll={shouldUpdateScroll}>
<ScrollContainer scrollKey='account_gallery'>
<div className='scrollable scrollable--flex' onScroll={this.handleScroll}>
<HeaderContainer accountId={this.props.params.accountId} />

@ -50,7 +50,6 @@ class AccountTimeline extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
statusIds: ImmutablePropTypes.list,
featuredStatusIds: ImmutablePropTypes.list,
isLoading: PropTypes.bool,
@ -115,7 +114,7 @@ class AccountTimeline extends ImmutablePureComponent {
}
render () {
const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, suspended, isAccount, multiColumn, remote, remoteUrl } = this.props;
const { statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, suspended, isAccount, multiColumn, remote, remoteUrl } = this.props;
if (!isAccount) {
return (
@ -162,7 +161,6 @@ class AccountTimeline extends ImmutablePureComponent {
isLoading={isLoading}
hasMore={hasMore}
onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
timelineId='account'

@ -29,7 +29,6 @@ class Blocks extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
isLoading: PropTypes.bool,
@ -46,7 +45,7 @@ class Blocks extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
const { intl, accountIds, shouldUpdateScroll, hasMore, multiColumn, isLoading } = this.props;
const { intl, accountIds, hasMore, multiColumn, isLoading } = this.props;
if (!accountIds) {
return (
@ -66,7 +65,6 @@ class Blocks extends ImmutablePureComponent {
onLoadMore={this.handleLoadMore}
hasMore={hasMore}
isLoading={isLoading}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
>

@ -27,7 +27,6 @@ class Bookmarks extends ImmutablePureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
statusIds: ImmutablePropTypes.list.isRequired,
intl: PropTypes.object.isRequired,
columnId: PropTypes.string,
@ -68,7 +67,7 @@ class Bookmarks extends ImmutablePureComponent {
}, 300, { leading: true })
render () {
const { intl, shouldUpdateScroll, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
const pinned = !!columnId;
const emptyMessage = <FormattedMessage id='empty_column.bookmarked_statuses' defaultMessage="You don't have any bookmarked toots yet. When you bookmark one, it will show up here." />;
@ -93,7 +92,6 @@ class Bookmarks extends ImmutablePureComponent {
hasMore={hasMore}
isLoading={isLoading}
onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
/>

@ -41,7 +41,6 @@ class CommunityTimeline extends React.PureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
columnId: PropTypes.string,
intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool,
@ -103,7 +102,7 @@ class CommunityTimeline extends React.PureComponent {
}
render () {
const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
const pinned = !!columnId;
return (
@ -127,7 +126,6 @@ class CommunityTimeline extends React.PureComponent {
timelineId={`community${onlyMedia ? ':media' : ''}`}
onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
shouldUpdateScroll={shouldUpdateScroll}
bindToDocument={!multiColumn}
/>
</Column>

@ -14,7 +14,6 @@ export default class ConversationsList extends ImmutablePureComponent {
hasMore: PropTypes.bool,
isLoading: PropTypes.bool,
onLoadMore: PropTypes.func,
shouldUpdateScroll: PropTypes.func,
};
getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id)

@ -19,7 +19,6 @@ class DirectTimeline extends React.PureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
columnId: PropTypes.string,
intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool,
@ -71,7 +70,7 @@ class DirectTimeline extends React.PureComponent {
}
render () {
const { intl, hasUnread, columnId, multiColumn, shouldUpdateScroll } = this.props;
const { intl, hasUnread, columnId, multiColumn } = this.props;
const pinned = !!columnId;
return (
@ -93,7 +92,6 @@ class DirectTimeline extends React.PureComponent {
timelineId='direct'
onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
shouldUpdateScroll={shouldUpdateScroll}
/>
</Column>
);

@ -12,7 +12,7 @@ import AccountCard from './components/account_card';
import RadioButton from 'mastodon/components/radio_button';
import classNames from 'classnames';
import LoadMore from 'mastodon/components/load_more';
import { ScrollContainer } from 'react-router-scroll-4';
import ScrollContainer from 'mastodon/containers/scroll_container';
const messages = defineMessages({
title: { id: 'column.directory', defaultMessage: 'Browse profiles' },
@ -40,7 +40,6 @@ class Directory extends React.PureComponent {
isLoading: PropTypes.bool,
accountIds: ImmutablePropTypes.list.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
columnId: PropTypes.string,
intl: PropTypes.object.isRequired,
multiColumn: PropTypes.bool,
@ -125,7 +124,7 @@ class Directory extends React.PureComponent {
}
render () {
const { isLoading, accountIds, intl, columnId, multiColumn, domain, shouldUpdateScroll } = this.props;
const { isLoading, accountIds, intl, columnId, multiColumn, domain } = this.props;
const { order, local } = this.getParams(this.props, this.state);
const pinned = !!columnId;
@ -163,7 +162,7 @@ class Directory extends React.PureComponent {
multiColumn={multiColumn}
/>
{multiColumn && !pinned ? <ScrollContainer scrollKey='directory' shouldUpdateScroll={shouldUpdateScroll}>{scrollableArea}</ScrollContainer> : scrollableArea}
{multiColumn && !pinned ? <ScrollContainer scrollKey='directory'>{scrollableArea}</ScrollContainer> : scrollableArea}
</Column>
);
}

@ -29,7 +29,6 @@ class Blocks extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
hasMore: PropTypes.bool,
domains: ImmutablePropTypes.orderedSet,
intl: PropTypes.object.isRequired,
@ -45,7 +44,7 @@ class Blocks extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
const { intl, domains, shouldUpdateScroll, hasMore, multiColumn } = this.props;
const { intl, domains, hasMore, multiColumn } = this.props;
if (!domains) {
return (
@ -64,7 +63,6 @@ class Blocks extends ImmutablePureComponent {
scrollKey='domain_blocks'
onLoadMore={this.handleLoadMore}
hasMore={hasMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
>

@ -27,7 +27,6 @@ class Favourites extends ImmutablePureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
statusIds: ImmutablePropTypes.list.isRequired,
intl: PropTypes.object.isRequired,
columnId: PropTypes.string,
@ -68,7 +67,7 @@ class Favourites extends ImmutablePureComponent {
}, 300, { leading: true })
render () {
const { intl, shouldUpdateScroll, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
const pinned = !!columnId;
const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favourite toots yet. When you favourite one, it will show up here." />;
@ -93,7 +92,6 @@ class Favourites extends ImmutablePureComponent {
hasMore={hasMore}
isLoading={isLoading}
onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
/>

@ -27,7 +27,6 @@ class Favourites extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
multiColumn: PropTypes.bool,
intl: PropTypes.object.isRequired,
@ -50,7 +49,7 @@ class Favourites extends ImmutablePureComponent {
}
render () {
const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props;
const { intl, accountIds, multiColumn } = this.props;
if (!accountIds) {
return (
@ -74,7 +73,6 @@ class Favourites extends ImmutablePureComponent {
<ScrollableList
scrollKey='favourites'
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
>

@ -32,7 +32,6 @@ class FollowRequests extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
hasMore: PropTypes.bool,
isLoading: PropTypes.bool,
accountIds: ImmutablePropTypes.list,
@ -51,7 +50,7 @@ class FollowRequests extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
const { intl, shouldUpdateScroll, accountIds, hasMore, multiColumn, locked, domain, isLoading } = this.props;
const { intl, accountIds, hasMore, multiColumn, locked, domain, isLoading } = this.props;
if (!accountIds) {
return (
@ -80,7 +79,6 @@ class FollowRequests extends ImmutablePureComponent {
onLoadMore={this.handleLoadMore}
hasMore={hasMore}
isLoading={isLoading}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
prepend={unlockedPrependMessage}

@ -43,7 +43,6 @@ class Followers extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
isLoading: PropTypes.bool,
@ -73,7 +72,7 @@ class Followers extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props;
const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props;
if (!isAccount) {
return (
@ -112,7 +111,6 @@ class Followers extends ImmutablePureComponent {
hasMore={hasMore}
isLoading={isLoading}
onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll}
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
alwaysPrepend
append={remoteMessage}

@ -43,7 +43,6 @@ class Following extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
isLoading: PropTypes.bool,
@ -73,7 +72,7 @@ class Following extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props;
const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props;
if (!isAccount) {
return (
@ -112,7 +111,6 @@ class Following extends ImmutablePureComponent {
hasMore={hasMore}
isLoading={isLoading}
onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll}
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
alwaysPrepend
append={remoteMessage}

@ -24,7 +24,6 @@ class HashtagTimeline extends React.PureComponent {
params: PropTypes.object.isRequired,
columnId: PropTypes.string,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
hasUnread: PropTypes.bool,
multiColumn: PropTypes.bool,
};
@ -130,7 +129,7 @@ class HashtagTimeline extends React.PureComponent {
}
render () {
const { shouldUpdateScroll, hasUnread, columnId, multiColumn } = this.props;
const { hasUnread, columnId, multiColumn } = this.props;
const { id, local } = this.props.params;
const pinned = !!columnId;
@ -156,7 +155,6 @@ class HashtagTimeline extends React.PureComponent {
timelineId={`hashtag:${id}${local ? ':local' : ''}`}
onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
shouldUpdateScroll={shouldUpdateScroll}
bindToDocument={!multiColumn}
/>
</Column>

@ -34,7 +34,6 @@ class HomeTimeline extends React.PureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool,
isPartial: PropTypes.bool,
@ -112,7 +111,7 @@ class HomeTimeline extends React.PureComponent {
}
render () {
const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
const pinned = !!columnId;
let announcementsButton = null;
@ -154,7 +153,6 @@ class HomeTimeline extends React.PureComponent {
onLoadMore={this.handleLoadMore}
timelineId='home'
emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage='Your home timeline is empty! Follow more people to fill it up. {suggestions}' values={{ suggestions: <Link to='/start'><FormattedMessage id='empty_column.home.suggestions' defaultMessage='See some suggestions' /></Link> }} />}
shouldUpdateScroll={shouldUpdateScroll}
bindToDocument={!multiColumn}
/>
</Column>

@ -41,7 +41,6 @@ class ListTimeline extends React.PureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
columnId: PropTypes.string,
hasUnread: PropTypes.bool,
multiColumn: PropTypes.bool,
@ -142,7 +141,7 @@ class ListTimeline extends React.PureComponent {
}
render () {
const { shouldUpdateScroll, hasUnread, columnId, multiColumn, list, intl } = this.props;
const { hasUnread, columnId, multiColumn, list, intl } = this.props;
const { id } = this.props.params;
const pinned = !!columnId;
const title = list ? list.get('title') : id;
@ -207,7 +206,6 @@ class ListTimeline extends React.PureComponent {
timelineId={`list:${id}`}
onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.' />}
shouldUpdateScroll={shouldUpdateScroll}
bindToDocument={!multiColumn}
/>
</Column>

@ -48,7 +48,7 @@ class Lists extends ImmutablePureComponent {
}
render () {
const { intl, shouldUpdateScroll, lists, multiColumn } = this.props;
const { intl, lists, multiColumn } = this.props;
if (!lists) {
return (
@ -68,7 +68,6 @@ class Lists extends ImmutablePureComponent {
<ScrollableList
scrollKey='lists'
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
prepend={<ColumnSubheading text={intl.formatMessage(messages.subheading)} />}
bindToDocument={!multiColumn}

@ -29,7 +29,6 @@ class Mutes extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
hasMore: PropTypes.bool,
isLoading: PropTypes.bool,
accountIds: ImmutablePropTypes.list,
@ -46,7 +45,7 @@ class Mutes extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
const { intl, shouldUpdateScroll, hasMore, accountIds, multiColumn, isLoading } = this.props;
const { intl, hasMore, accountIds, multiColumn, isLoading } = this.props;
if (!accountIds) {
return (
@ -66,7 +65,6 @@ class Mutes extends ImmutablePureComponent {
onLoadMore={this.handleLoadMore}
hasMore={hasMore}
isLoading={isLoading}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
>

@ -74,7 +74,6 @@ class Notifications extends React.PureComponent {
notifications: ImmutablePropTypes.list.isRequired,
showFilterBar: PropTypes.bool.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
intl: PropTypes.object.isRequired,
isLoading: PropTypes.bool,
isUnread: PropTypes.bool,
@ -176,7 +175,7 @@ class Notifications extends React.PureComponent {
};
render () {
const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props;
const { intl, notifications, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props;
const pinned = !!columnId;
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. When other people interact with you, you will see it here." />;
@ -227,7 +226,6 @@ class Notifications extends React.PureComponent {
onLoadPending={this.handleLoadPending}
onScrollToTop={this.handleScrollToTop}
onScroll={this.handleScroll}
shouldUpdateScroll={shouldUpdateScroll}
bindToDocument={!multiColumn}
>
{scrollableContent}

@ -24,7 +24,6 @@ class PinnedStatuses extends ImmutablePureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
statusIds: ImmutablePropTypes.list.isRequired,
intl: PropTypes.object.isRequired,
hasMore: PropTypes.bool.isRequired,
@ -44,7 +43,7 @@ class PinnedStatuses extends ImmutablePureComponent {
}
render () {
const { intl, shouldUpdateScroll, statusIds, hasMore, multiColumn } = this.props;
const { intl, statusIds, hasMore, multiColumn } = this.props;
return (
<Column bindToDocument={!multiColumn} icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}>
@ -53,7 +52,6 @@ class PinnedStatuses extends ImmutablePureComponent {
statusIds={statusIds}
scrollKey='pinned_statuses'
hasMore={hasMore}
shouldUpdateScroll={shouldUpdateScroll}
bindToDocument={!multiColumn}
/>
</Column>

@ -43,7 +43,6 @@ class PublicTimeline extends React.PureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
intl: PropTypes.object.isRequired,
columnId: PropTypes.string,
multiColumn: PropTypes.bool,
@ -106,7 +105,7 @@ class PublicTimeline extends React.PureComponent {
}
render () {
const { intl, shouldUpdateScroll, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props;
const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props;
const pinned = !!columnId;
return (
@ -130,7 +129,6 @@ class PublicTimeline extends React.PureComponent {
trackScroll={!pinned}
scrollKey={`public_timeline-${columnId}`}
emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up' />}
shouldUpdateScroll={shouldUpdateScroll}
bindToDocument={!multiColumn}
/>
</Column>

@ -27,7 +27,6 @@ class Reblogs extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
multiColumn: PropTypes.bool,
intl: PropTypes.object.isRequired,
@ -50,7 +49,7 @@ class Reblogs extends ImmutablePureComponent {
}
render () {
const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props;
const { intl, accountIds, multiColumn } = this.props;
if (!accountIds) {
return (
@ -74,7 +73,6 @@ class Reblogs extends ImmutablePureComponent {
<ScrollableList
scrollKey='reblogs'
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
>

@ -45,7 +45,7 @@ import { initBlockModal } from '../../actions/blocks';
import { initBoostModal } from '../../actions/boosts';
import { initReport } from '../../actions/reports';
import { makeGetStatus, makeGetPictureInPicture } from '../../selectors';
import { ScrollContainer } from 'react-router-scroll-4';
import ScrollContainer from 'mastodon/containers/scroll_container';
import ColumnBackButton from '../../components/column_back_button';
import ColumnHeader from '../../components/column_header';
import StatusContainer from '../../containers/status_container';
@ -498,7 +498,7 @@ class Status extends ImmutablePureComponent {
render () {
let ancestors, descendants;
const { shouldUpdateScroll, status, ancestorsIds, descendantsIds, intl, domain, multiColumn, pictureInPicture } = this.props;
const { status, ancestorsIds, descendantsIds, intl, domain, multiColumn, pictureInPicture } = this.props;
const { fullscreen } = this.state;
if (status === null) {
@ -541,7 +541,7 @@ class Status extends ImmutablePureComponent {
)}
/>
<ScrollContainer scrollKey='thread' shouldUpdateScroll={shouldUpdateScroll}>
<ScrollContainer scrollKey='thread'>
<div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
{ancestors}

@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
import Audio from 'mastodon/features/audio';
import { connect } from 'react-redux';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { previewState } from './video_modal';
import Footer from 'mastodon/features/picture_in_picture/components/footer';
const mapStateToProps = (state, { statusId }) => ({
@ -25,32 +24,6 @@ class AudioModal extends ImmutablePureComponent {
onChangeBackgroundColor: PropTypes.func.isRequired,
};
static contextTypes = {
router: PropTypes.object,
};
componentDidMount () {
if (this.context.router) {
const history = this.context.router.history;
history.push(history.location.pathname, previewState);
this.unlistenHistory = history.listen(() => {
this.props.onClose();
});
}
}
componentWillUnmount () {
if (this.context.router) {
this.unlistenHistory();
if (this.context.router.history.location.state === previewState) {
this.context.router.history.goBack();
}
}
}
render () {
const { media, accountStaticAvatar, statusId, onClose } = this.props;
const options = this.props.options || {};

@ -20,8 +20,6 @@ const messages = defineMessages({
next: { id: 'lightbox.next', defaultMessage: 'Next' },
});
export const previewState = 'previewMediaModal';
export default @injectIntl
class MediaModal extends ImmutablePureComponent {
@ -37,10 +35,6 @@ class MediaModal extends ImmutablePureComponent {
volume: PropTypes.number,
};
static contextTypes = {
router: PropTypes.object,
};
state = {
index: null,
navigationHidden: false,
@ -98,16 +92,6 @@ class MediaModal extends ImmutablePureComponent {
componentDidMount () {
window.addEventListener('keydown', this.handleKeyDown, false);
if (this.context.router) {
const history = this.context.router.history;
history.push(history.location.pathname, previewState);
this.unlistenHistory = history.listen(() => {
this.props.onClose();
});
}
this._sendBackgroundColor();
}
@ -131,14 +115,6 @@ class MediaModal extends ImmutablePureComponent {
componentWillUnmount () {
window.removeEventListener('keydown', this.handleKeyDown);
if (this.context.router) {
this.unlistenHistory();
if (this.context.router.history.location.state === previewState) {
this.context.router.history.goBack();
}
}
this.props.onChangeBackgroundColor(null);
}

@ -6,8 +6,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import Footer from 'mastodon/features/picture_in_picture/components/footer';
import { getAverageFromBlurhash } from 'mastodon/blurhash';
export const previewState = 'previewVideoModal';
export default class VideoModal extends ImmutablePureComponent {
static propTypes = {
@ -22,19 +20,9 @@ export default class VideoModal extends ImmutablePureComponent {
onChangeBackgroundColor: PropTypes.func.isRequired,
};
static contextTypes = {
router: PropTypes.object,
};
componentDidMount () {
const { router } = this.context;
const { media, onChangeBackgroundColor, onClose } = this.props;
if (router) {
router.history.push(router.history.location.pathname, previewState);
this.unlistenHistory = router.history.listen(() => onClose());
}
const backgroundColor = getAverageFromBlurhash(media.get('blurhash'));
if (backgroundColor) {
@ -42,18 +30,6 @@ export default class VideoModal extends ImmutablePureComponent {
}
}
componentWillUnmount () {
const { router } = this.context;
if (router) {
this.unlistenHistory();
if (router.history.location.state === previewState) {
router.history.goBack();
}
}
}
render () {
const { media, statusId, onClose } = this.props;
const options = this.props.options || {};

@ -3,8 +3,8 @@ import { closeModal } from '../../../actions/modal';
import ModalRoot from '../components/modal_root';
const mapStateToProps = state => ({
type: state.get('modal').modalType,
props: state.get('modal').modalProps,
type: state.getIn(['modal', 0, 'modalType'], null),
props: state.getIn(['modal', 0, 'modalProps'], {}),
});
const mapDispatchToProps = dispatch => ({

@ -54,8 +54,6 @@ import {
FollowRecommendations,
} from './util/async-components';
import { me } from '../../initial_state';
import { previewState as previewMediaState } from './components/media_modal';
import { previewState as previewVideoState } from './components/video_modal';
import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
// Dummy import, to make sure that <Status /> ends up in the application bundle.
@ -138,10 +136,6 @@ class SwitchingColumnsArea extends React.PureComponent {
}
}
shouldUpdateScroll (_, { location }) {
return location.state !== previewMediaState && location.state !== previewVideoState;
}
setRef = c => {
if (c) {
this.node = c.getWrappedInstance();
@ -158,38 +152,38 @@ class SwitchingColumnsArea extends React.PureComponent {
{redirect}
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/timelines/public/local' exact component={CommunityTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/notifications' component={Notifications} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
<WrappedRoute path='/timelines/public/local' exact component={CommunityTimeline} content={children} />
<WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} />
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
<WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} />
<WrappedRoute path='/notifications' component={Notifications} content={children} />
<WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} />
<WrappedRoute path='/bookmarks' component={BookmarkedStatuses} content={children} />
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
<WrappedRoute path='/start' component={FollowRecommendations} content={children} />
<WrappedRoute path='/search' component={Search} content={children} />
<WrappedRoute path='/directory' component={Directory} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/directory' component={Directory} content={children} />
<WrappedRoute path='/statuses/new' component={Compose} content={children} />
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll, withReplies: true }} />
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/blocks' component={Blocks} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/mutes' component={Mutes} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/lists' component={Lists} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} />
<WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} />
<WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} />
<WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
<WrappedRoute path='/blocks' component={Blocks} content={children} />
<WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} />
<WrappedRoute path='/mutes' component={Mutes} content={children} />
<WrappedRoute path='/lists' component={Lists} content={children} />
<WrappedRoute component={GenericNotFound} content={children} />
</WrappedSwitch>

@ -1,19 +1,15 @@
import { MODAL_OPEN, MODAL_CLOSE } from '../actions/modal';
import { TIMELINE_DELETE } from '../actions/timelines';
import { Stack as ImmutableStack, Map as ImmutableMap } from 'immutable';
const initialState = {
modalType: null,
modalProps: {},
};
export default function modal(state = initialState, action) {
export default function modal(state = ImmutableStack(), action) {
switch(action.type) {
case MODAL_OPEN:
return { modalType: action.modalType, modalProps: action.modalProps };
return state.unshift(ImmutableMap({ modalType: action.modalType, modalProps: action.modalProps }));
case MODAL_CLOSE:
return (action.modalType === undefined || action.modalType === state.modalType) ? initialState : state;
return (action.modalType === undefined || action.modalType === state.getIn([0, 'modalType'])) ? state.shift() : state;
case TIMELINE_DELETE:
return (state.modalProps.statusId === action.id) ? initialState : state;
return state.filterNot((modal) => modal.get('modalProps').statusId === action.id);
default:
return state;
}

@ -2,7 +2,7 @@
lock '3.16.0'
set :repo_url, ENV.fetch('REPO', 'https://github.com/tootsuite/mastodon.git')
set :repo_url, ENV.fetch('REPO', 'https://github.com/mastodon/mastodon.git')
set :branch, ENV.fetch('BRANCH', 'master')
set :application, 'mastodon'

@ -3,7 +3,7 @@ class ResetUniqueJobsLocks < ActiveRecord::Migration[5.2]
def up
# We do this to clean up unique job digests that were not properly
# disposed of prior to https://github.com/tootsuite/mastodon/pull/13361
# disposed of prior to https://github.com/mastodon/mastodon/pull/13361
until SidekiqUniqueJobs::Digests.new.delete_by_pattern('*').nil?; end
end

@ -1,6 +1,6 @@
# frozen_string_literal: true
REPOSITORY_NAME = 'tootsuite/mastodon'
REPOSITORY_NAME = 'mastodon/mastodon'
namespace :repo do
desc 'Generate the AUTHORS.md file'
@ -34,7 +34,7 @@ namespace :repo do
file << <<~FOOTER
This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/tootsuite/mastodon/graphs/contributors) instead.
This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/mastodon/mastodon/graphs/contributors) instead.
FOOTER
end

@ -1,5 +1,5 @@
{
"name": "@tootsuite/mastodon",
"name": "@mastodon/mastodon",
"license": "AGPL-3.0-or-later",
"engines": {
"node": ">=12"
@ -18,7 +18,7 @@
},
"repository": {
"type": "git",
"url": "https://github.com/tootsuite/mastodon.git"
"url": "https://github.com/mastodon/mastodon.git"
},
"browserslist": [
"last 2 versions",

@ -1,8 +1,8 @@
{
"name": "Mastodon",
"description": "A GNU Social-compatible microblogging server",
"repository": "https://github.com/tootsuite/mastodon",
"logo": "https://github.com/tootsuite.png",
"repository": "https://github.com/mastodon/mastodon",
"logo": "https://github.com/mastodon.png",
"env": {
"LOCAL_DOMAIN": {
"description": "The domain that your Mastodon instance will run on (this can be appname.scalingo.io or a custom domain)",

@ -1,261 +0,0 @@
<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia">
<id>http://kickass.zone/users/localhost.atom</id>
<title>::1</title>
<updated>2016-10-10T13:29:56Z</updated>
<logo>http://kickass.zone/system/accounts/avatars/000/000/001/medium/eris.png</logo>
<author>
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
<uri>http://kickass.zone/users/localhost</uri>
<name>localhost</name>
<email>localhost@kickass.zone</email>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost"/>
<link rel="avatar" type="image/png" media:width="300" media:height="300" href="http://kickass.zone/system/accounts/avatars/000/000/001/large/eris.png"/>
<link rel="avatar" type="image/png" media:width="96" media:height="96" href="http://kickass.zone/system/accounts/avatars/000/000/001/medium/eris.png"/>
<link rel="avatar" type="image/png" media:width="48" media:height="48" href="http://kickass.zone/system/accounts/avatars/000/000/001/small/eris.png"/>
<poco:preferredUsername>localhost</poco:preferredUsername>
<poco:displayName>::1</poco:displayName>
</author>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost"/>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost.atom"/>
<link rel="hub" href="https://pubsubhubbub.superfeedr.com"/>
<link rel="salmon" href="http://kickass.zone/api/salmon/1"/>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=7:objectType=Follow</id>
<published>2016-10-10T13:29:56Z</published>
<updated>2016-10-10T13:29:56Z</updated>
<title>localhost started following kat@mastodon.social</title>
<content type="html">localhost started following kat@mastodon.social</content>
<activity:verb>http://activitystrea.ms/schema/1.0/follow</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/12.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/12"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
<activity:object>
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
<uri>https://mastodon.social/users/kat</uri>
<name>kat</name>
<email>kat@mastodon.social</email>
<summary>#trans #queer</summary>
<link rel="alternate" type="text/html" href="https://mastodon.social/users/kat"/>
<link rel="avatar" type="image/jpeg" media:width="300" media:height="300" href="http://kickass.zone/system/accounts/avatars/000/000/016/large/kat-20150403T124737-b2mbt44.jpg"/>
<link rel="avatar" type="image/jpeg" media:width="96" media:height="96" href="http://kickass.zone/system/accounts/avatars/000/000/016/medium/kat-20150403T124737-b2mbt44.jpg"/>
<link rel="avatar" type="image/jpeg" media:width="48" media:height="48" href="http://kickass.zone/system/accounts/avatars/000/000/016/small/kat-20150403T124737-b2mbt44.jpg"/>
<poco:preferredUsername>kat</poco:preferredUsername>
<poco:displayName>Kat</poco:displayName>
<poco:note>#trans #queer</poco:note>
</activity:object>
</entry>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=3:objectType=Favourite</id>
<published>2016-10-10T13:29:26Z</published>
<updated>2016-10-10T13:29:26Z</updated>
<title>localhost favourited a status by kat@mastodon.social</title>
<content type="html">localhost favourited a status by kat@mastodon.social</content>
<activity:verb>http://activitystrea.ms/schema/1.0/favorite</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/11.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/11"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
<thr:in-reply-to ref="tag:mastodon.social,2016-10-10:objectId=22833:objectType=Status" href="https://mastodon.social/users/kat/updates/16543" type="text/html"/>
<activity:object>
<activity:object-type>http://activitystrea.ms/schema/1.0/comment</activity:object-type>
<id>tag:mastodon.social,2016-10-10:objectId=22833:objectType=Status</id>
<title>@localhost oooh more mastodons &#x2764;</title>
<link rel="alternate" type="text/html" href="https://mastodon.social/users/kat/updates/16543"/>
<content type="html">&lt;p&gt;&lt;a href="http://kickass.zone/users/localhost"&gt;@localhost&lt;/a&gt; oooh more mastodons &#x2764;&lt;/p&gt;</content>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<published>2016-10-10T13:23:35Z</published>
<updated>2016-10-10T13:23:35Z</updated>
<author>
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
<uri>https://mastodon.social/users/kat</uri>
<name>kat</name>
<email>kat@mastodon.social</email>
<summary>#trans #queer</summary>
<link rel="alternate" type="text/html" href="https://mastodon.social/users/kat"/>
<link rel="avatar" type="image/jpeg" media:width="300" media:height="300" href="http://kickass.zone/system/accounts/avatars/000/000/016/large/kat-20150403T124737-b2mbt44.jpg"/>
<link rel="avatar" type="image/jpeg" media:width="96" media:height="96" href="http://kickass.zone/system/accounts/avatars/000/000/016/medium/kat-20150403T124737-b2mbt44.jpg"/>
<link rel="avatar" type="image/jpeg" media:width="48" media:height="48" href="http://kickass.zone/system/accounts/avatars/000/000/016/small/kat-20150403T124737-b2mbt44.jpg"/>
<poco:preferredUsername>kat</poco:preferredUsername>
<poco:displayName>Kat</poco:displayName>
<poco:note>#trans #queer</poco:note>
</author>
<link rel="mentioned" href="http://kickass.zone/users/localhost"/>
</activity:object>
</entry>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=2:objectType=Favourite</id>
<published>2016-10-10T13:13:15Z</published>
<updated>2016-10-10T13:13:15Z</updated>
<title>localhost favourited a status by Gargron@mastodon.social</title>
<content type="html">localhost favourited a status by Gargron@mastodon.social</content>
<activity:verb>http://activitystrea.ms/schema/1.0/favorite</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/10.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/10"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
<thr:in-reply-to ref="tag:mastodon.social,2016-10-10:objectId=22825:objectType=Status" href="https://mastodon.social/users/Gargron/updates/16538" type="text/html"/>
<activity:object>
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
<id>tag:mastodon.social,2016-10-10:objectId=22825:objectType=Status</id>
<title>Deployed some fixes</title>
<link rel="alternate" type="text/html" href="https://mastodon.social/users/Gargron/updates/16538"/>
<content type="html">&lt;p&gt;Deployed some fixes&lt;/p&gt;</content>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<published>2016-10-10T13:10:37Z</published>
<updated>2016-10-10T13:10:37Z</updated>
<author>
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
<uri>https://mastodon.social/users/Gargron</uri>
<name>Gargron</name>
<email>Gargron@mastodon.social</email>
<summary>Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon</summary>
<link rel="alternate" type="text/html" href="https://mastodon.social/users/Gargron"/>
<link rel="avatar" type="image/png" media:width="300" media:height="300" href="http://kickass.zone/system/accounts/avatars/000/000/003/large/4375_eugencommish.png"/>
<link rel="avatar" type="image/png" media:width="96" media:height="96" href="http://kickass.zone/system/accounts/avatars/000/000/003/medium/4375_eugencommish.png"/>
<link rel="avatar" type="image/png" media:width="48" media:height="48" href="http://kickass.zone/system/accounts/avatars/000/000/003/small/4375_eugencommish.png"/>
<poco:preferredUsername>Gargron</poco:preferredUsername>
<poco:displayName>Eugen</poco:displayName>
<poco:note>Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon</poco:note>
</author>
</activity:object>
</entry>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=17:objectType=Status</id>
<published>2016-10-10T00:41:31Z</published>
<updated>2016-10-10T00:41:31Z</updated>
<title>Social media needs MOAR cats! http://kickass.zone/media/3</title>
<content type="html">&lt;p&gt;Social media needs MOAR cats! &lt;a rel="nofollow noopener noreferrer" href="http://kickass.zone/media/3"&gt;http://kickass.zone/media/3&lt;/a&gt;&lt;/p&gt;</content>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/9.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/9"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
<link rel="enclosure" href="http://kickass.zone/system/media_attachments/files/000/000/003/original/gizmo.jpg?1476060065" type="image/jpeg" length="108841"/>
</entry>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=14:objectType=Status</id>
<published>2016-10-10T00:38:39Z</published>
<updated>2016-10-10T00:38:39Z</updated>
<title>http://kickass.zone/media/2</title>
<content type="html">&lt;p&gt;&lt;a rel="nofollow noopener noreferrer" href="http://kickass.zone/media/2"&gt;http://kickass.zone/media/2&lt;/a&gt;&lt;/p&gt;</content>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/8.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/8"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
<link rel="enclosure" href="http://kickass.zone/system/media_attachments/files/000/000/002/original/morpheus_linux.jpg?1476059910" type="image/jpeg" length="191816"/>
</entry>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=12:objectType=Status</id>
<published>2016-10-10T00:37:49Z</published>
<updated>2016-10-10T00:37:49Z</updated>
<title/>
<activity:verb>http://activitystrea.ms/schema/1.0/delete</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/7.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/7"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
</entry>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=4:objectType=Follow</id>
<published>2016-10-10T00:23:07Z</published>
<updated>2016-10-10T00:23:07Z</updated>
<title>localhost started following bignimbus@mastodon.social</title>
<content type="html">localhost started following bignimbus@mastodon.social</content>
<activity:verb>http://activitystrea.ms/schema/1.0/follow</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/6.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/6"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
<activity:object>
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
<uri>https://mastodon.social/users/bignimbus</uri>
<name>bignimbus</name>
<email>bignimbus@mastodon.social</email>
<summary>jdauriemma.com</summary>
<link rel="alternate" type="text/html" href="https://mastodon.social/users/bignimbus"/>
<link rel="avatar" type="image/png" media:width="300" media:height="300" href="http://kickass.zone/system/accounts/avatars/000/000/004/large/jeff_avatar.png"/>
<link rel="avatar" type="image/png" media:width="96" media:height="96" href="http://kickass.zone/system/accounts/avatars/000/000/004/medium/jeff_avatar.png"/>
<link rel="avatar" type="image/png" media:width="48" media:height="48" href="http://kickass.zone/system/accounts/avatars/000/000/004/small/jeff_avatar.png"/>
<poco:preferredUsername>bignimbus</poco:preferredUsername>
<poco:displayName>Jeff Auriemma</poco:displayName>
<poco:note>jdauriemma.com</poco:note>
</activity:object>
</entry>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=2:objectType=Follow</id>
<published>2016-10-10T00:14:18Z</published>
<updated>2016-10-10T00:14:18Z</updated>
<title>localhost started following Gargron@mastodon.social</title>
<content type="html">localhost started following Gargron@mastodon.social</content>
<activity:verb>http://activitystrea.ms/schema/1.0/follow</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/5.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/5"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
<activity:object>
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
<uri>https://mastodon.social/users/Gargron</uri>
<name>Gargron</name>
<email>Gargron@mastodon.social</email>
<summary>Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon</summary>
<link rel="alternate" type="text/html" href="https://mastodon.social/users/Gargron"/>
<link rel="avatar" type="image/png" media:width="300" media:height="300" href="http://kickass.zone/system/accounts/avatars/000/000/003/large/4375_eugencommish.png"/>
<link rel="avatar" type="image/png" media:width="96" media:height="96" href="http://kickass.zone/system/accounts/avatars/000/000/003/medium/4375_eugencommish.png"/>
<link rel="avatar" type="image/png" media:width="48" media:height="48" href="http://kickass.zone/system/accounts/avatars/000/000/003/small/4375_eugencommish.png"/>
<poco:preferredUsername>Gargron</poco:preferredUsername>
<poco:displayName>Eugen</poco:displayName>
<poco:note>Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon</poco:note>
</activity:object>
</entry>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=1:objectType=Follow</id>
<published>2016-10-10T00:09:09Z</published>
<updated>2016-10-10T00:09:09Z</updated>
<title>localhost started following abc@mastodon.social</title>
<content type="html">localhost started following abc@mastodon.social</content>
<activity:verb>http://activitystrea.ms/schema/1.0/follow</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/4.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/4"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
<activity:object>
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
<uri>https://mastodon.social/users/abc</uri>
<name>abc</name>
<email>abc@mastodon.social</email>
<link rel="alternate" type="text/html" href="https://mastodon.social/users/abc"/>
<link rel="avatar" type="image/jpeg" media:width="300" media:height="300" href="http://kickass.zone/system/accounts/avatars/000/000/002/large/cbm64_80x80.jpg"/>
<link rel="avatar" type="image/jpeg" media:width="96" media:height="96" href="http://kickass.zone/system/accounts/avatars/000/000/002/medium/cbm64_80x80.jpg"/>
<link rel="avatar" type="image/jpeg" media:width="48" media:height="48" href="http://kickass.zone/system/accounts/avatars/000/000/002/small/cbm64_80x80.jpg"/>
<poco:preferredUsername>abc</poco:preferredUsername>
<poco:displayName>abc</poco:displayName>
</activity:object>
</entry>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=3:objectType=Status</id>
<published>2016-10-10T00:02:47Z</published>
<updated>2016-10-10T00:02:47Z</updated>
<title/>
<activity:verb>http://activitystrea.ms/schema/1.0/delete</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/3.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/3"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
</entry>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=2:objectType=Status</id>
<published>2016-10-10T00:02:18Z</published>
<updated>2016-10-10T00:02:18Z</updated>
<title>Yes, that was the obligatory first post. :)</title>
<content type="html">&lt;p&gt;Yes, that was the obligatory first post. :)&lt;/p&gt;</content>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/2.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/2"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/comment</activity:object-type>
<thr:in-reply-to ref="tag:kickass.zone,2016-10-10:objectId=1:objectType=Status" href="http://kickass.zone/users/localhost/updates/1" type="text/html"/>
</entry>
<entry>
<id>tag:kickass.zone,2016-10-10:objectId=1:objectType=Status</id>
<published>2016-10-10T00:01:56Z</published>
<updated>2016-10-10T00:01:56Z</updated>
<title>Hello, world!</title>
<content type="html">&lt;p&gt;Hello, world!&lt;/p&gt;</content>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/1.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/1"/>
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
</entry>
</feed>

@ -19,7 +19,7 @@ describe 'about/show.html.haml', without_verify_partial_doubles: true do
site_short_description: 'something',
site_description: 'something',
version_number: '1.0',
source_url: 'https://github.com/tootsuite/mastodon',
source_url: 'https://github.com/mastodon/mastodon',
open_registrations: false,
thumbnail: nil,
hero: nil,

Loading…
Cancel
Save