From f129d9c49bdd95eae9c8da5d2dae95d9af74c83a Mon Sep 17 00:00:00 2001
From: mgabdev <>
Date: Thu, 29 Oct 2020 18:46:54 -0500
Subject: [PATCH] Added LinkTimeline and PreviewCard fetching by id
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
• Added:
- LinkTimeline and PreviewCard fetching by id
---
app/controllers/api/v1/links_controller.rb | 17 +++
.../v1/timelines/preview_card_controller.rb | 62 ++++++++++
app/javascript/gabsocial/actions/links.js | 30 +++++
app/javascript/gabsocial/actions/streaming.js | 1 +
app/javascript/gabsocial/actions/timelines.js | 1 +
.../gabsocial/features/link_timeline.js | 108 ++++++++++++++++++
app/javascript/gabsocial/features/ui/ui.js | 4 +
.../features/ui/util/async_components.js | 1 +
app/javascript/gabsocial/pages/link_page.js | 46 ++++++++
app/javascript/gabsocial/reducers/index.js | 2 +
app/javascript/gabsocial/reducers/links.js | 40 +++++++
.../rest/preview_card_serializer.rb | 2 +-
config/routes.rb | 2 +
config/settings.yml | 2 +
streaming/index.js | 8 ++
15 files changed, 325 insertions(+), 1 deletion(-)
create mode 100644 app/controllers/api/v1/links_controller.rb
create mode 100644 app/controllers/api/v1/timelines/preview_card_controller.rb
create mode 100644 app/javascript/gabsocial/actions/links.js
create mode 100644 app/javascript/gabsocial/features/link_timeline.js
create mode 100644 app/javascript/gabsocial/pages/link_page.js
create mode 100644 app/javascript/gabsocial/reducers/links.js
diff --git a/app/controllers/api/v1/links_controller.rb b/app/controllers/api/v1/links_controller.rb
new file mode 100644
index 00000000..87d0a10a
--- /dev/null
+++ b/app/controllers/api/v1/links_controller.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class Api::V1::LinksController < Api::BaseController
+ before_action :require_user!
+ before_action :set_link
+
+ def show
+ render json: @link, serializer: REST::PreviewCardSerializer
+ end
+
+ private
+
+ def set_link
+ @link = PreviewCard.find(params[:id])
+ end
+
+end
diff --git a/app/controllers/api/v1/timelines/preview_card_controller.rb b/app/controllers/api/v1/timelines/preview_card_controller.rb
new file mode 100644
index 00000000..8f0d15c2
--- /dev/null
+++ b/app/controllers/api/v1/timelines/preview_card_controller.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+class Api::V1::Timelines::PreviewCardController < Api::BaseController
+ before_action :require_user!
+ before_action :set_link
+ before_action :set_statuses
+
+ after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
+
+ def show
+ render json: @statuses,
+ each_serializer: REST::StatusSerializer,
+ relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id)
+ end
+
+ private
+
+ def set_link
+ @link = PreviewCard.find(params[:id])
+end
+
+ def set_statuses
+ @statuses = cached_link_statuses
+ end
+
+ def cached_link_statuses
+ cache_collection link_statuses, Status
+ end
+
+ def link_statuses
+ statuses = Status.joins(
+ "LEFT JOIN preview_cards_statuses ON statuses.id = preview_cards_statuses.status_id"
+ ).where("preview_cards_statuses.preview_card_id": params[:id]).paginate_by_id(
+ limit_param(DEFAULT_STATUSES_LIMIT),
+ params_slice(:max_id, :since_id, :min_id)
+ ).reject { |status| FeedManager.instance.filter?(:home, status, current_account.id) }
+ end
+
+ def insert_pagination_headers
+ set_pagination_headers(next_path, prev_path)
+ end
+
+ def pagination_params(core_params)
+ params.slice(:limit).permit(:limit).merge(core_params)
+ end
+
+ def next_path
+ api_v1_timelines_preview_card_url params[:id], pagination_params(max_id: pagination_max_id)
+ end
+
+ def prev_path
+ api_v1_timelines_preview_card_url params[:id], pagination_params(min_id: pagination_since_id)
+ end
+
+ def pagination_max_id
+ @statuses.last.id
+ end
+
+ def pagination_since_id
+ @statuses.first.id
+ end
+end
diff --git a/app/javascript/gabsocial/actions/links.js b/app/javascript/gabsocial/actions/links.js
new file mode 100644
index 00000000..4954ef1f
--- /dev/null
+++ b/app/javascript/gabsocial/actions/links.js
@@ -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,
+})
\ No newline at end of file
diff --git a/app/javascript/gabsocial/actions/streaming.js b/app/javascript/gabsocial/actions/streaming.js
index ea43aecd..5530e8b9 100644
--- a/app/javascript/gabsocial/actions/streaming.js
+++ b/app/javascript/gabsocial/actions/streaming.js
@@ -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}`);
diff --git a/app/javascript/gabsocial/actions/timelines.js b/app/javascript/gabsocial/actions/timelines.js
index ddbcd914..300bb1bf 100644
--- a/app/javascript/gabsocial/actions/timelines.js
+++ b/app/javascript/gabsocial/actions/timelines.js
@@ -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,
diff --git a/app/javascript/gabsocial/features/link_timeline.js b/app/javascript/gabsocial/features/link_timeline.js
new file mode 100644
index 00000000..a580a491
--- /dev/null
+++ b/app/javascript/gabsocial/features/link_timeline.js
@@ -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