forked from cybrespace/mastodon
		
	Add Pinned toot column (#4817)
* Add Pinned_toot_section * Fix add frozen_string_literal * Fix delete no need controller and tests * Fix replace query strings to axios params * Fix change value to accountId and disabling more button
This commit is contained in:
		
							parent
							
								
									8185f98872
								
							
						
					
					
						commit
						85c7c42098
					
				
					 10 changed files with 135 additions and 3 deletions
				
			
		
							
								
								
									
										39
									
								
								app/javascript/mastodon/actions/pin_statuses.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								app/javascript/mastodon/actions/pin_statuses.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
import api from '../api';
 | 
			
		||||
 | 
			
		||||
export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST';
 | 
			
		||||
export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS';
 | 
			
		||||
export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL';
 | 
			
		||||
 | 
			
		||||
export function fetchPinnedStatuses() {
 | 
			
		||||
  return (dispatch, getState) => {
 | 
			
		||||
    dispatch(fetchPinnedStatusesRequest());
 | 
			
		||||
 | 
			
		||||
    const accountId = getState().getIn(['meta', 'me']);
 | 
			
		||||
    api(getState).get(`/api/v1/accounts/${accountId}/statuses`, { params: { pinned: true } }).then(response => {
 | 
			
		||||
      dispatch(fetchPinnedStatusesSuccess(response.data, null));
 | 
			
		||||
    }).catch(error => {
 | 
			
		||||
      dispatch(fetchPinnedStatusesFail(error));
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function fetchPinnedStatusesRequest() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: PINNED_STATUSES_FETCH_REQUEST,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function fetchPinnedStatusesSuccess(statuses, next) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: PINNED_STATUSES_FETCH_SUCCESS,
 | 
			
		||||
    statuses,
 | 
			
		||||
    next,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function fetchPinnedStatusesFail(error) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: PINNED_STATUSES_FETCH_FAIL,
 | 
			
		||||
    error,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ const messages = defineMessages({
 | 
			
		|||
  blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
 | 
			
		||||
  mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
 | 
			
		||||
  info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' },
 | 
			
		||||
  pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const mapStateToProps = state => ({
 | 
			
		||||
| 
						 | 
				
			
			@ -66,15 +67,16 @@ export default class GettingStarted extends ImmutablePureComponent {
 | 
			
		|||
 | 
			
		||||
    navItems = navItems.concat([
 | 
			
		||||
      <ColumnLink key='4' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
 | 
			
		||||
      <ColumnLink key='5' icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />,
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    if (me.get('locked')) {
 | 
			
		||||
      navItems.push(<ColumnLink key='5' icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' />);
 | 
			
		||||
      navItems.push(<ColumnLink key='6' icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' />);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    navItems = navItems.concat([
 | 
			
		||||
      <ColumnLink key='6' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />,
 | 
			
		||||
      <ColumnLink key='7' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />,
 | 
			
		||||
      <ColumnLink key='7' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />,
 | 
			
		||||
      <ColumnLink key='8' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />,
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										59
									
								
								app/javascript/mastodon/features/pinned_statuses/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								app/javascript/mastodon/features/pinned_statuses/index.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
import React from 'react';
 | 
			
		||||
import { connect } from 'react-redux';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
			
		||||
import { fetchPinnedStatuses } from '../../actions/pin_statuses';
 | 
			
		||||
import Column from '../ui/components/column';
 | 
			
		||||
import ColumnBackButtonSlim from '../../components/column_back_button_slim';
 | 
			
		||||
import StatusList from '../../components/status_list';
 | 
			
		||||
import { defineMessages, injectIntl } from 'react-intl';
 | 
			
		||||
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
			
		||||
 | 
			
		||||
const messages = defineMessages({
 | 
			
		||||
  heading: { id: 'column.pins', defaultMessage: 'Pinned toot' },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const mapStateToProps = state => ({
 | 
			
		||||
  statusIds: state.getIn(['status_lists', 'pins', 'items']),
 | 
			
		||||
  hasMore: !!state.getIn(['status_lists', 'pins', 'next']),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@connect(mapStateToProps)
 | 
			
		||||
@injectIntl
 | 
			
		||||
export default class PinnedStatuses extends ImmutablePureComponent {
 | 
			
		||||
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    dispatch: PropTypes.func.isRequired,
 | 
			
		||||
    statusIds: ImmutablePropTypes.list.isRequired,
 | 
			
		||||
    intl: PropTypes.object.isRequired,
 | 
			
		||||
    hasMore: PropTypes.bool.isRequired,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  componentWillMount () {
 | 
			
		||||
    this.props.dispatch(fetchPinnedStatuses());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleHeaderClick = () => {
 | 
			
		||||
    this.column.scrollTop();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setRef = c => {
 | 
			
		||||
    this.column = c;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render () {
 | 
			
		||||
    const { intl, statusIds, hasMore } = this.props;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <Column icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}>
 | 
			
		||||
        <ColumnBackButtonSlim />
 | 
			
		||||
        <StatusList
 | 
			
		||||
          statusIds={statusIds}
 | 
			
		||||
          scrollKey='pinned_statuses'
 | 
			
		||||
          hasMore={hasMore}
 | 
			
		||||
        />
 | 
			
		||||
      </Column>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +35,7 @@ import {
 | 
			
		|||
  FavouritedStatuses,
 | 
			
		||||
  Blocks,
 | 
			
		||||
  Mutes,
 | 
			
		||||
  PinnedStatuses,
 | 
			
		||||
} from './util/async-components';
 | 
			
		||||
 | 
			
		||||
// Dummy import, to make sure that <Status /> ends up in the application bundle.
 | 
			
		||||
| 
						 | 
				
			
			@ -208,6 +209,7 @@ export default class UI extends React.PureComponent {
 | 
			
		|||
 | 
			
		||||
            <WrappedRoute path='/notifications' component={Notifications} content={children} />
 | 
			
		||||
            <WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} />
 | 
			
		||||
            <WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
 | 
			
		||||
 | 
			
		||||
            <WrappedRoute path='/statuses/new' component={Compose} content={children} />
 | 
			
		||||
            <WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,6 +34,10 @@ export function GettingStarted () {
 | 
			
		|||
  return import(/* webpackChunkName: "features/getting_started" */'../../getting_started');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function PinnedStatuses () {
 | 
			
		||||
  return import(/* webpackChunkName: "features/pinned_statuses" */'../../pinned_statuses');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function AccountTimeline () {
 | 
			
		||||
  return import(/* webpackChunkName: "features/account_timeline" */'../../account_timeline');
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,6 +34,7 @@
 | 
			
		|||
  "column.mutes": "Muted users",
 | 
			
		||||
  "column.notifications": "Notifications",
 | 
			
		||||
  "column.public": "Federated timeline",
 | 
			
		||||
  "column.pins": "Pinned toots",
 | 
			
		||||
  "column_back_button.label": "Back",
 | 
			
		||||
  "column_header.hide_settings": "Hide settings",
 | 
			
		||||
  "column_header.moveLeft_settings": "Move column to the left",
 | 
			
		||||
| 
						 | 
				
			
			@ -111,6 +112,7 @@
 | 
			
		|||
  "navigation_bar.mutes": "Muted users",
 | 
			
		||||
  "navigation_bar.preferences": "Preferences",
 | 
			
		||||
  "navigation_bar.public_timeline": "Federated timeline",
 | 
			
		||||
  "navigation_bar.pins": "Pinned toots",
 | 
			
		||||
  "notification.favourite": "{name} favourited your status",
 | 
			
		||||
  "notification.follow": "{name} followed you",
 | 
			
		||||
  "notification.mention": "{name} mentioned you",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,6 +34,7 @@
 | 
			
		|||
  "column.mutes": "ミュートしたユーザー",
 | 
			
		||||
  "column.notifications": "通知",
 | 
			
		||||
  "column.public": "連合タイムライン",
 | 
			
		||||
  "column.pins": "固定されたトゥート",
 | 
			
		||||
  "column_back_button.label": "戻る",
 | 
			
		||||
  "column_header.hide_settings": "設定を隠す",
 | 
			
		||||
  "column_header.moveLeft_settings": "カラムを左に移動する",
 | 
			
		||||
| 
						 | 
				
			
			@ -111,6 +112,7 @@
 | 
			
		|||
  "navigation_bar.mutes": "ミュートしたユーザー",
 | 
			
		||||
  "navigation_bar.preferences": "ユーザー設定",
 | 
			
		||||
  "navigation_bar.public_timeline": "連合タイムライン",
 | 
			
		||||
  "navigation_bar.pins": "固定されたトゥート",
 | 
			
		||||
  "notification.favourite": "{name}さんがあなたのトゥートをお気に入りに登録しました",
 | 
			
		||||
  "notification.follow": "{name}さんにフォローされました",
 | 
			
		||||
  "notification.mention": "{name}さんがあなたに返信しました",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,6 +34,7 @@
 | 
			
		|||
  "column.mutes": "뮤트 중인 사용자",
 | 
			
		||||
  "column.notifications": "알림",
 | 
			
		||||
  "column.public": "연합 타임라인",
 | 
			
		||||
  "column.pins": "고정된 Toot",
 | 
			
		||||
  "column_back_button.label": "돌아가기",
 | 
			
		||||
  "column_header.hide_settings": "Hide settings",
 | 
			
		||||
  "column_header.moveLeft_settings": "Move column to the left",
 | 
			
		||||
| 
						 | 
				
			
			@ -111,6 +112,7 @@
 | 
			
		|||
  "navigation_bar.mutes": "뮤트 중인 사용자",
 | 
			
		||||
  "navigation_bar.preferences": "사용자 설정",
 | 
			
		||||
  "navigation_bar.public_timeline": "연합 타임라인",
 | 
			
		||||
  "navigation_bar.pins": "고정된 Toot",
 | 
			
		||||
  "notification.favourite": "{name}님이 즐겨찾기 했습니다",
 | 
			
		||||
  "notification.follow": "{name}님이 나를 팔로우 했습니다",
 | 
			
		||||
  "notification.mention": "{name}님이 답글을 보냈습니다",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,10 +2,15 @@ import {
 | 
			
		|||
  FAVOURITED_STATUSES_FETCH_SUCCESS,
 | 
			
		||||
  FAVOURITED_STATUSES_EXPAND_SUCCESS,
 | 
			
		||||
} from '../actions/favourites';
 | 
			
		||||
import {
 | 
			
		||||
  PINNED_STATUSES_FETCH_SUCCESS,
 | 
			
		||||
} from '../actions/pin_statuses';
 | 
			
		||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
 | 
			
		||||
import {
 | 
			
		||||
  FAVOURITE_SUCCESS,
 | 
			
		||||
  UNFAVOURITE_SUCCESS,
 | 
			
		||||
  PIN_SUCCESS,
 | 
			
		||||
  UNPIN_SUCCESS,
 | 
			
		||||
} from '../actions/interactions';
 | 
			
		||||
 | 
			
		||||
const initialState = ImmutableMap({
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +19,11 @@ const initialState = ImmutableMap({
 | 
			
		|||
    loaded: false,
 | 
			
		||||
    items: ImmutableList(),
 | 
			
		||||
  }),
 | 
			
		||||
  pins: ImmutableMap({
 | 
			
		||||
    next: null,
 | 
			
		||||
    loaded: false,
 | 
			
		||||
    items: ImmutableList(),
 | 
			
		||||
  }),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const normalizeList = (state, listType, statuses, next) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +63,12 @@ export default function statusLists(state = initialState, action) {
 | 
			
		|||
    return prependOneToList(state, 'favourites', action.status);
 | 
			
		||||
  case UNFAVOURITE_SUCCESS:
 | 
			
		||||
    return removeOneFromList(state, 'favourites', action.status);
 | 
			
		||||
  case PINNED_STATUSES_FETCH_SUCCESS:
 | 
			
		||||
    return normalizeList(state, 'pins', action.statuses, action.next);
 | 
			
		||||
  case PIN_SUCCESS:
 | 
			
		||||
    return prependOneToList(state, 'pins', action.status);
 | 
			
		||||
  case UNPIN_SUCCESS:
 | 
			
		||||
    return removeOneFromList(state, 'pins', action.status);
 | 
			
		||||
  default:
 | 
			
		||||
    return state;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,9 @@ import {
 | 
			
		|||
  FAVOURITED_STATUSES_FETCH_SUCCESS,
 | 
			
		||||
  FAVOURITED_STATUSES_EXPAND_SUCCESS,
 | 
			
		||||
} from '../actions/favourites';
 | 
			
		||||
import {
 | 
			
		||||
  PINNED_STATUSES_FETCH_SUCCESS,
 | 
			
		||||
} from '../actions/pin_statuses';
 | 
			
		||||
import { SEARCH_FETCH_SUCCESS } from '../actions/search';
 | 
			
		||||
import emojify from '../emoji';
 | 
			
		||||
import { Map as ImmutableMap, fromJS } from 'immutable';
 | 
			
		||||
| 
						 | 
				
			
			@ -138,6 +141,7 @@ export default function statuses(state = initialState, action) {
 | 
			
		|||
  case NOTIFICATIONS_EXPAND_SUCCESS:
 | 
			
		||||
  case FAVOURITED_STATUSES_FETCH_SUCCESS:
 | 
			
		||||
  case FAVOURITED_STATUSES_EXPAND_SUCCESS:
 | 
			
		||||
  case PINNED_STATUSES_FETCH_SUCCESS:
 | 
			
		||||
  case SEARCH_FETCH_SUCCESS:
 | 
			
		||||
    return normalizeStatuses(state, action.statuses);
 | 
			
		||||
  case TIMELINE_DELETE:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue