parent
62292797ec
commit
48b9619439
@ -0,0 +1,72 @@ |
||||
import { connect } from 'react-redux'; |
||||
import PureRenderMixin from 'react-addons-pure-render-mixin'; |
||||
import StatusListContainer from '../ui/containers/status_list_container'; |
||||
import Column from '../ui/components/column'; |
||||
import { |
||||
refreshTimeline, |
||||
updateTimeline |
||||
} from '../../actions/timelines'; |
||||
|
||||
const HashtagTimeline = React.createClass({ |
||||
|
||||
propTypes: { |
||||
params: React.PropTypes.object.isRequired, |
||||
dispatch: React.PropTypes.func.isRequired |
||||
}, |
||||
|
||||
mixins: [PureRenderMixin], |
||||
|
||||
_subscribe (dispatch, id) { |
||||
if (typeof App !== 'undefined') { |
||||
this.subscription = App.cable.subscriptions.create({ |
||||
channel: 'HashtagChannel', |
||||
tag: id |
||||
}, { |
||||
|
||||
received (data) { |
||||
dispatch(updateTimeline('tag', JSON.parse(data.message))); |
||||
} |
||||
|
||||
}); |
||||
} |
||||
}, |
||||
|
||||
_unsubscribe () { |
||||
if (typeof this.subscription !== 'undefined') { |
||||
this.subscription.unsubscribe(); |
||||
} |
||||
}, |
||||
|
||||
componentWillMount () { |
||||
const { dispatch } = this.props; |
||||
const { id } = this.props.params; |
||||
|
||||
dispatch(refreshTimeline('tag', true, id)); |
||||
this._subscribe(dispatch, id); |
||||
}, |
||||
|
||||
componentWillReceiveProps (nextProps) { |
||||
if (nextProps.params.id !== this.props.params.id) { |
||||
this.props.dispatch(refreshTimeline('tag', true, nextProps.params.id)); |
||||
this._unsubscribe(); |
||||
this._subscribe(this.props.dispatch, nextProps.params.id); |
||||
} |
||||
}, |
||||
|
||||
componentWillUnmount () { |
||||
this._unsubscribe(); |
||||
}, |
||||
|
||||
render () { |
||||
const { id } = this.props.params; |
||||
|
||||
return ( |
||||
<Column icon='hashtag' heading={id}> |
||||
<StatusListContainer type='tag' id={id} /> |
||||
</Column> |
||||
); |
||||
}, |
||||
|
||||
}); |
||||
|
||||
export default connect()(HashtagTimeline); |
@ -1,4 +1,17 @@ |
||||
module ApplicationCable |
||||
class Channel < ActionCable::Channel::Base |
||||
protected |
||||
|
||||
def hydrate_status(encoded_message) |
||||
message = ActiveSupport::JSON.decode(encoded_message) |
||||
status = Status.find_by(id: message['id']) |
||||
message['message'] = FeedManager.instance.inline_render(current_user.account, status) |
||||
|
||||
[status, message] |
||||
end |
||||
|
||||
def filter?(status) |
||||
status.nil? || current_user.account.blocking?(status.account) || (status.reblog? && current_user.account.blocking?(status.reblog.account)) |
||||
end |
||||
end |
||||
end |
||||
|
@ -0,0 +1,11 @@ |
||||
class HashtagChannel < ApplicationCable::Channel |
||||
def subscribed |
||||
tag = params[:tag].downcase |
||||
|
||||
stream_from "timeline:hashtag:#{tag}", lambda { |encoded_message| |
||||
status, message = hydrate_status(encoded_message) |
||||
next if filter?(status) |
||||
transmit message |
||||
} |
||||
end |
||||
end |
@ -1,19 +1,9 @@ |
||||
# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. |
||||
class PublicChannel < ApplicationCable::Channel |
||||
def subscribed |
||||
stream_from 'timeline:public', lambda { |encoded_message| |
||||
message = ActiveSupport::JSON.decode(encoded_message) |
||||
|
||||
status = Status.find_by(id: message['id']) |
||||
next if status.nil? || current_user.account.blocking?(status.account) || (status.reblog? && current_user.account.blocking?(status.reblog.account)) |
||||
|
||||
message['message'] = FeedManager.instance.inline_render(current_user.account, status) |
||||
|
||||
status, message = hydrate_status(encoded_message) |
||||
next if filter?(status) |
||||
transmit message |
||||
} |
||||
end |
||||
|
||||
def unsubscribed |
||||
# Any cleanup needed when channel is unsubscribed |
||||
end |
||||
end |
||||
|
@ -0,0 +1,4 @@ |
||||
class TagsController < ApplicationController |
||||
def show |
||||
end |
||||
end |
@ -0,0 +1,2 @@ |
||||
module TagsHelper |
||||
end |
@ -1,5 +1,11 @@ |
||||
class Tag < ApplicationRecord |
||||
has_and_belongs_to_many :statuses |
||||
|
||||
HASHTAG_RE = /[?:^|\s|\.|>]#([[:word:]_]+)/i |
||||
|
||||
validates :name, presence: true, uniqueness: true |
||||
|
||||
def to_param |
||||
name |
||||
end |
||||
end |
||||
|
@ -0,0 +1,11 @@ |
||||
class ProcessHashtagsService < BaseService |
||||
def call(status, tags = []) |
||||
if status.local? |
||||
tags = status.text.scan(Tag::HASHTAG_RE).map(&:first) |
||||
end |
||||
|
||||
tags.map(&:downcase).each do |tag| |
||||
status.tags << Tag.where(name: tag).first_or_initialize(name: tag) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,2 @@ |
||||
attribute :name |
||||
node(:url) { |tag| tag_url(tag) } |
@ -0,0 +1,8 @@ |
||||
class CreateStatusesTagsJoinTable < ActiveRecord::Migration[5.0] |
||||
def change |
||||
create_join_table :statuses, :tags do |t| |
||||
t.index :tag_id |
||||
t.index [:tag_id, :status_id], unique: true |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,12 @@ |
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe TagsController, type: :controller do |
||||
|
||||
describe 'GET #show' do |
||||
it 'returns http success' do |
||||
get :show, params: { id: 'test' } |
||||
expect(response).to have_http_status(:success) |
||||
end |
||||
end |
||||
|
||||
end |
@ -0,0 +1,5 @@ |
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe TagsHelper, type: :helper do |
||||
|
||||
end |
Loading…
Reference in new issue