Added LinkTimeline and PreviewCard fetching by id
• Added: - LinkTimeline and PreviewCard fetching by id
This commit is contained in:
30
app/javascript/gabsocial/actions/links.js
Normal file
30
app/javascript/gabsocial/actions/links.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import api from '../api'
|
||||
|
||||
export const LINK_FETCH_REQUEST = 'LINK_FETCH_REQUEST'
|
||||
export const LINK_FETCH_SUCCESS = 'LINK_FETCH_SUCCESS'
|
||||
export const LINK_FETCH_FAIL = 'LINK_FETCH_FAIL'
|
||||
|
||||
export const fetchLinkCard = (cardId) => (dispatch, getState) => {
|
||||
dispatch(fetchLinkCardRequest(cardId))
|
||||
|
||||
api(getState).get(`/api/v1/links/${cardId}`).then(({ data }) => {
|
||||
dispatch(fetchLinkCardSuccess(data))
|
||||
})
|
||||
.catch((err) => dispatch(fetchLinkCardFail(err)))
|
||||
}
|
||||
|
||||
export const fetchLinkCardRequest = (cardId) => ({
|
||||
type: LINK_FETCH_REQUEST,
|
||||
cardId,
|
||||
})
|
||||
|
||||
export const fetchLinkCardSuccess = (card) => ({
|
||||
type: LINK_FETCH_SUCCESS,
|
||||
card,
|
||||
})
|
||||
|
||||
export const fetchLinkCardFail = (error, cardId) => ({
|
||||
type: LINK_FETCH_FAIL,
|
||||
error,
|
||||
cardId,
|
||||
})
|
||||
@@ -52,6 +52,7 @@ export function connectTimelineStream (timelineId, path, pollingRefresh = null,
|
||||
|
||||
export const connectUserStream = () => connectTimelineStream('home', 'user');
|
||||
export const connectProStream = () => connectTimelineStream('pro', 'pro');
|
||||
export const connectLinkStream = (linkId, accept) => connectTimelineStream(`link:${linkId}`, `link&linkId=${linkId}`, null, accept);
|
||||
export const connectHashtagStream = (id, tag, accept) => connectTimelineStream(`hashtag:${id}`, `hashtag&tag=${tag}`, null, accept);
|
||||
export const connectListStream = id => connectTimelineStream(`list:${id}`, `list&list=${id}`);
|
||||
export const connectGroupStream = id => connectTimelineStream(`group:${id}`, `group&group=${id}`);
|
||||
|
||||
@@ -176,6 +176,7 @@ export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTim
|
||||
export const expandGroupTimeline = (id, { sortBy, maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`group:${id}`, `/api/v1/timelines/group/${id}`, { sort_by: sortBy, max_id: maxId, only_media: onlyMedia }, done);
|
||||
export const expandGroupFeaturedTimeline = (groupId, done = noOp) => expandTimeline(`group:${groupId}:pinned`, `/api/v1/timelines/group_pins/${groupId}`, {}, done);
|
||||
export const expandGroupCollectionTimeline = (collectionType, { sortBy, maxId } = {}, done = noOp) => expandTimeline(`group_collection:${collectionType}`, `/api/v1/timelines/group_collection/${collectionType}`, { sort_by: sortBy, max_id: maxId }, done);
|
||||
export const expandLinkTimeline = (linkId, { maxId } = {}, done = noOp) => expandTimeline(`link:${linkId}`, `/api/v1/timelines/preview_card/${linkId}`, { max_id: maxId }, done);
|
||||
export const expandHashtagTimeline = (hashtag, { maxId, tags } = {}, done = noOp) => {
|
||||
return expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, {
|
||||
max_id: maxId,
|
||||
|
||||
108
app/javascript/gabsocial/features/link_timeline.js
Normal file
108
app/javascript/gabsocial/features/link_timeline.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { FormattedMessage } from 'react-intl'
|
||||
import { connectLinkStream } from '../actions/streaming'
|
||||
import { expandLinkTimeline } from '../actions/timelines'
|
||||
import { fetchLinkCard } from '../actions/links'
|
||||
import { openModal } from '../actions/modal'
|
||||
import StatusList from '../components/status_list'
|
||||
import ColumnIndicator from '../components/column_indicator'
|
||||
import Button from '../components/button'
|
||||
import Text from '../components/text'
|
||||
|
||||
class LinkTimeline extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.handleConnect(this.props.params.id)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.handleDisconnect()
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.params.id !== this.props.params.id) {
|
||||
this.handleDisconnect()
|
||||
this.handleConnect(nextProps.params.id)
|
||||
}
|
||||
}
|
||||
|
||||
handleConnect(id) {
|
||||
const { dispatch } = this.props
|
||||
|
||||
dispatch(fetchLinkCard(id))
|
||||
dispatch(expandLinkTimeline(id))
|
||||
|
||||
this.disconnect = dispatch(connectLinkStream(id))
|
||||
}
|
||||
|
||||
handleDisconnect() {
|
||||
if (this.disconnect) {
|
||||
this.disconnect()
|
||||
this.disconnect = null
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = (maxId) => {
|
||||
const { id } = this.props.params
|
||||
this.props.dispatch(expandLinkTimeline(id, { maxId }))
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
link,
|
||||
items,
|
||||
isFetched,
|
||||
isLoading,
|
||||
} = this.props
|
||||
const { id } = this.props.params
|
||||
|
||||
if (typeof link === 'undefined' && isLoading) {
|
||||
return <ColumnIndicator type='loading' />
|
||||
} else if (!link) {
|
||||
return <ColumnIndicator type='missing' />
|
||||
}
|
||||
|
||||
const emptyMessage = (
|
||||
<div className={[_s.d, _s.py15, _s.px15, _s.aiCenter].join(' ')}>
|
||||
<Text>No statuses with this url yet.</Text>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<StatusList
|
||||
scrollKey='link_timeline'
|
||||
timelineId={`link:${id}`}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={emptyMessage}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
items: state.getIn(['links', 'items']),
|
||||
link: state.getIn(['links', 'items', `${props.params.id}`]),
|
||||
isFetched: state.getIn(['links', 'isFetched']),
|
||||
isLoading: state.getIn(['links', 'isLoading']),
|
||||
})
|
||||
|
||||
LinkTimeline.propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
link: PropTypes.oneOfType([
|
||||
ImmutablePropTypes.map,
|
||||
PropTypes.bool,
|
||||
]),
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(LinkTimeline)
|
||||
@@ -44,6 +44,7 @@ import ProPage from '../../pages/pro_page'
|
||||
import ExplorePage from '../../pages/explore_page'
|
||||
import NewsPage from '../../pages/news_page'
|
||||
import AboutPage from '../../pages/about_page'
|
||||
import LinkPage from '../../pages/link_page'
|
||||
|
||||
import {
|
||||
About,
|
||||
@@ -77,6 +78,7 @@ import {
|
||||
HomeTimeline,
|
||||
Investors,
|
||||
LikedStatuses,
|
||||
LinkTimeline,
|
||||
ListCreate,
|
||||
ListsDirectory,
|
||||
ListEdit,
|
||||
@@ -213,6 +215,8 @@ class SwitchingArea extends React.PureComponent {
|
||||
<WrappedRoute path='/groups/:id' exact publicRoute page={GroupPage} component={GroupTimeline} content={children} componentParams={{ isTimeline: true }} />
|
||||
|
||||
<WrappedRoute path='/tags/:id' publicRoute page={HashtagPage} component={HashtagTimeline} content={children} componentParams={{ title: 'Hashtag' }} />
|
||||
|
||||
<WrappedRoute path='/links/:id' page={LinkPage} component={LinkTimeline} content={children} componentParams={{ title: 'Links' }} />
|
||||
|
||||
<WrappedRoute path='/shortcuts' page={ShortcutsPage} component={Shortcuts} content={children} />
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ export function HotkeysModal() { return import(/* webpackChunkName: "components/
|
||||
export function Introduction() { return import(/* webpackChunkName: "features/introduction" */'../../introduction') }
|
||||
export function Investors() { return import(/* webpackChunkName: "features/about/investors" */'../../about/investors') }
|
||||
export function LinkFooter() { return import(/* webpackChunkName: "components/link_footer" */'../../../components/link_footer') }
|
||||
export function LinkTimeline() { return import(/* webpackChunkName: "features/link_timeline" */'../../link_timeline') }
|
||||
export function ListAddUserModal() { return import(/* webpackChunkName: "features/list_add_user_modal" */'../../../components/modal/list_add_user_modal') }
|
||||
export function ListCreate() { return import(/* webpackChunkName: "features/list_create" */'../../list_create') }
|
||||
export function ListCreateModal() { return import(/* webpackChunkName: "components/list_create_modal" */'../../../components/modal/list_create_modal') }
|
||||
|
||||
46
app/javascript/gabsocial/pages/link_page.js
Normal file
46
app/javascript/gabsocial/pages/link_page.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import PageTitle from '../features/ui/util/page_title'
|
||||
import DefaultLayout from '../layouts/default_layout'
|
||||
import {
|
||||
LinkFooter,
|
||||
TrendsPanel,
|
||||
UserSuggestionsPanel,
|
||||
} from '../features/ui/util/async_components'
|
||||
|
||||
class LinkPage extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
const {
|
||||
children,
|
||||
page,
|
||||
title,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<DefaultLayout
|
||||
noComposeButton
|
||||
showBackBtn
|
||||
title={title}
|
||||
page={page}
|
||||
layout={[
|
||||
TrendsPanel,
|
||||
UserSuggestionsPanel,
|
||||
LinkFooter,
|
||||
]}
|
||||
>
|
||||
<PageTitle path={title} />
|
||||
{children}
|
||||
</DefaultLayout>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LinkPage.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
page: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
export default LinkPage
|
||||
@@ -15,6 +15,7 @@ import group_lists from './group_lists'
|
||||
import group_relationships from './group_relationships'
|
||||
import hashtags from './hashtags'
|
||||
import height_cache from './height_cache'
|
||||
import links from './links.js'
|
||||
import lists from './lists'
|
||||
import listAdder from './list_adder'
|
||||
import listEditor from './list_editor'
|
||||
@@ -59,6 +60,7 @@ const reducers = {
|
||||
group_relationships,
|
||||
hashtags,
|
||||
height_cache,
|
||||
links,
|
||||
lists,
|
||||
listAdder,
|
||||
listEditor,
|
||||
|
||||
40
app/javascript/gabsocial/reducers/links.js
Normal file
40
app/javascript/gabsocial/reducers/links.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
Map as ImmutableMap,
|
||||
List as ImmutableList,
|
||||
fromJS,
|
||||
} from 'immutable'
|
||||
import {
|
||||
LINK_FETCH_REQUEST,
|
||||
LINK_FETCH_SUCCESS,
|
||||
LINK_FETCH_FAIL,
|
||||
} from '../actions/links'
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
isFetched: false,
|
||||
isError: false,
|
||||
isLoading: false,
|
||||
items: ImmutableMap(),
|
||||
})
|
||||
|
||||
export default function links(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case LINK_FETCH_REQUEST:
|
||||
return state.set('isLoading', true)
|
||||
case LINK_FETCH_SUCCESS:
|
||||
return state.withMutations((mutable) => {
|
||||
mutable.setIn(['items', `${action.card.id}`], fromJS(action.card))
|
||||
mutable.set('isLoading', false)
|
||||
mutable.set('isFetched', false)
|
||||
mutable.set('isError', false)
|
||||
})
|
||||
case LINK_FETCH_FAIL:
|
||||
return state.withMutations((mutable) => {
|
||||
mutable.set('isLoading', false)
|
||||
mutable.set('isFetched', false)
|
||||
mutable.set('isError', true)
|
||||
})
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user