Преглед на файлове

Display list column (#5750)

master
Eugen Rochko преди 6 години
committed by GitHub
родител
ревизия
31ac5f0e00
No known key found for this signature in database GPG ключ ID: 4AEE18F83AFDEB23
променени са 10 файла, в които са добавени 166 реда и са изтрити 1 реда
  1. +28
    -0
      app/javascript/mastodon/actions/lists.js
  2. +1
    -0
      app/javascript/mastodon/actions/streaming.js
  3. +2
    -0
      app/javascript/mastodon/actions/timelines.js
  4. +106
    -0
      app/javascript/mastodon/features/list_timeline/index.js
  5. +2
    -1
      app/javascript/mastodon/features/ui/components/columns_area.js
  6. +2
    -0
      app/javascript/mastodon/features/ui/index.js
  7. +4
    -0
      app/javascript/mastodon/features/ui/util/async-components.js
  8. +2
    -0
      app/javascript/mastodon/reducers/index.js
  9. +15
    -0
      app/javascript/mastodon/reducers/lists.js
  10. +4
    -0
      app/serializers/rest/list_serializer.rb

+ 28
- 0
app/javascript/mastodon/actions/lists.js Целия файл

@@ -0,0 +1,28 @@
import api from '../api';

export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST';
export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS';
export const LIST_FETCH_FAIL = 'LIST_FETCH_FAIL';

export const fetchList = id => (dispatch, getState) => {
dispatch(fetchListRequest(id));

api(getState).get(`/api/v1/lists/${id}`)
.then(({ data }) => dispatch(fetchListSuccess(data)))
.catch(err => dispatch(fetchListFail(err)));
};

export const fetchListRequest = id => ({
type: LIST_FETCH_REQUEST,
id,
});

export const fetchListSuccess = list => ({
type: LIST_FETCH_SUCCESS,
list,
});

export const fetchListFail = error => ({
type: LIST_FETCH_FAIL,
error,
});

+ 1
- 0
app/javascript/mastodon/actions/streaming.js Целия файл

@@ -51,3 +51,4 @@ export const connectCommunityStream = () => connectTimelineStream('community', '
export const connectMediaStream = () => connectTimelineStream('community', 'public:local');
export const connectPublicStream = () => connectTimelineStream('public', 'public');
export const connectHashtagStream = (tag) => connectTimelineStream(`hashtag:${tag}`, `hashtag&tag=${tag}`);
export const connectListStream = (id) => connectTimelineStream(`list:${id}`, `list&list=${id}`);

+ 2
- 0
app/javascript/mastodon/actions/timelines.js Целия файл

@@ -118,6 +118,7 @@ export const refreshCommunityTimeline = () => refreshTimeline('community', '/
export const refreshAccountTimeline = accountId => refreshTimeline(`account:${accountId}`, `/api/v1/accounts/${accountId}/statuses`);
export const refreshAccountMediaTimeline = accountId => refreshTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
export const refreshHashtagTimeline = hashtag => refreshTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
export const refreshListTimeline = id => refreshTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`);

export function refreshTimelineFail(timeline, error, skipLoading) {
return {
@@ -158,6 +159,7 @@ export const expandCommunityTimeline = () => expandTimeline('community', '/ap
export const expandAccountTimeline = accountId => expandTimeline(`account:${accountId}`, `/api/v1/accounts/${accountId}/statuses`);
export const expandAccountMediaTimeline = accountId => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
export const expandHashtagTimeline = hashtag => expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
export const expandListTimeline = id => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`);

export function expandTimelineRequest(timeline) {
return {


+ 106
- 0
app/javascript/mastodon/features/list_timeline/index.js Целия файл

@@ -0,0 +1,106 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { FormattedMessage } from 'react-intl';
import { connectListStream } from '../../actions/streaming';
import { refreshListTimeline, expandListTimeline } from '../../actions/timelines';
import { fetchList } from '../../actions/lists';

const mapStateToProps = (state, props) => ({
list: state.getIn(['lists', props.params.id]),
hasUnread: state.getIn(['timelines', `list:${props.params.id}`, 'unread']) > 0,
});

@connect(mapStateToProps)
export default class ListTimeline extends React.PureComponent {

static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
columnId: PropTypes.string,
hasUnread: PropTypes.bool,
multiColumn: PropTypes.bool,
list: ImmutablePropTypes.map,
};

handlePin = () => {
const { columnId, dispatch } = this.props;

if (columnId) {
dispatch(removeColumn(columnId));
} else {
dispatch(addColumn('LIST', { id: this.props.params.id }));
}
}

handleMove = (dir) => {
const { columnId, dispatch } = this.props;
dispatch(moveColumn(columnId, dir));
}

handleHeaderClick = () => {
this.column.scrollTop();
}

componentDidMount () {
const { dispatch } = this.props;
const { id } = this.props.params;

dispatch(fetchList(id));
dispatch(refreshListTimeline(id));

this.disconnect = dispatch(connectListStream(id));
}

componentWillUnmount () {
if (this.disconnect) {
this.disconnect();
this.disconnect = null;
}
}

setRef = c => {
this.column = c;
}

handleLoadMore = () => {
const { id } = this.props.params;
this.props.dispatch(expandListTimeline(id));
}

render () {
const { hasUnread, columnId, multiColumn, list } = this.props;
const { id } = this.props.params;
const pinned = !!columnId;
const title = list ? list.get('title') : id;

return (
<Column ref={this.setRef}>
<ColumnHeader
icon='bars'
active={hasUnread}
title={title}
onPin={this.handlePin}
onMove={this.handleMove}
onClick={this.handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
/>

<StatusListContainer
trackScroll={!pinned}
scrollKey={`list_timeline-${columnId}`}
timelineId={`list:${id}`}
loadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet.' />}
/>
</Column>
);
}

}

+ 2
- 1
app/javascript/mastodon/features/ui/components/columns_area.js Целия файл

@@ -11,7 +11,7 @@ import BundleContainer from '../containers/bundle_container';
import ColumnLoading from './column_loading';
import DrawerLoading from './drawer_loading';
import BundleColumnError from './bundle_column_error';
import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, FavouritedStatuses } from '../../ui/util/async-components';
import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, FavouritedStatuses, ListTimeline } from '../../ui/util/async-components';

import detectPassiveEvents from 'detect-passive-events';
import { scrollRight } from '../../../scroll';
@@ -24,6 +24,7 @@ const componentMap = {
'COMMUNITY': CommunityTimeline,
'HASHTAG': HashtagTimeline,
'FAVOURITES': FavouritedStatuses,
'LIST': ListTimeline,
};

@component => injectIntl(component, { withRef: true })


+ 2
- 0
app/javascript/mastodon/features/ui/index.js Целия файл

@@ -33,6 +33,7 @@ import {
FollowRequests,
GenericNotFound,
FavouritedStatuses,
ListTimeline,
Blocks,
Mutes,
PinnedStatuses,
@@ -372,6 +373,7 @@ export default class UI extends React.Component {
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
<WrappedRoute path='/timelines/public/local' component={CommunityTimeline} 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} />


+ 4
- 0
app/javascript/mastodon/features/ui/util/async-components.js Целия файл

@@ -26,6 +26,10 @@ export function HashtagTimeline () {
return import(/* webpackChunkName: "features/hashtag_timeline" */'../../hashtag_timeline');
}

export function ListTimeline () {
return import(/* webpackChunkName: "features/list_timeline" */'../../list_timeline');
}

export function Status () {
return import(/* webpackChunkName: "features/status" */'../../status');
}


+ 2
- 0
app/javascript/mastodon/reducers/index.js Целия файл

@@ -22,6 +22,7 @@ import media_attachments from './media_attachments';
import notifications from './notifications';
import height_cache from './height_cache';
import custom_emojis from './custom_emojis';
import lists from './lists';

const reducers = {
timelines,
@@ -47,6 +48,7 @@ const reducers = {
notifications,
height_cache,
custom_emojis,
lists,
};

export default combineReducers(reducers);

+ 15
- 0
app/javascript/mastodon/reducers/lists.js Целия файл

@@ -0,0 +1,15 @@
import { LIST_FETCH_SUCCESS } from '../actions/lists';
import { Map as ImmutableMap, fromJS } from 'immutable';

const initialState = ImmutableMap();

const normalizeList = (state, list) => state.set(list.id, fromJS(list));

export default function lists(state = initialState, action) {
switch(action.type) {
case LIST_FETCH_SUCCESS:
return normalizeList(state, action.list);
default:
return state;
}
};

+ 4
- 0
app/serializers/rest/list_serializer.rb Целия файл

@@ -2,4 +2,8 @@

class REST::ListSerializer < ActiveModel::Serializer
attributes :id, :title

def id
object.id.to_s
end
end

Зареждане…
Отказ
Запис