Follow requests send e-mail notifications, but are excluded from notifications API
Better initial state for unlisted/nsfw togglesgh/stable
parent
3689c119f0
commit
2146ac91a0
|
@ -20,6 +20,7 @@ const messages = defineMessages({
|
||||||
const ComposeForm = React.createClass({
|
const ComposeForm = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
intl: React.PropTypes.object.isRequired,
|
||||||
text: React.PropTypes.string.isRequired,
|
text: React.PropTypes.string.isRequired,
|
||||||
suggestion_token: React.PropTypes.string,
|
suggestion_token: React.PropTypes.string,
|
||||||
suggestions: ImmutablePropTypes.list,
|
suggestions: ImmutablePropTypes.list,
|
||||||
|
@ -129,7 +130,7 @@ const ComposeForm = React.createClass({
|
||||||
<span style={{ display: 'inline-block', verticalAlign: 'middle', marginBottom: '14px', marginLeft: '8px', color: '#9baec8' }}><FormattedMessage id='compose_form.private' defaultMessage='Mark as private' /></span>
|
<span style={{ display: 'inline-block', verticalAlign: 'middle', marginBottom: '14px', marginLeft: '8px', color: '#9baec8' }}><FormattedMessage id='compose_form.private' defaultMessage='Mark as private' /></span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<Motion defaultStyle={{ opacity: 100, height: 39.5 }} style={{ opacity: spring(this.props.private ? 0 : 100), height: spring(this.props.private ? 0 : 39.5) }}>
|
<Motion defaultStyle={{ opacity: this.props.private ? 0 : 100, height: this.props.private ? 39.5 : 0 }} style={{ opacity: spring(this.props.private ? 0 : 100), height: spring(this.props.private ? 0 : 39.5) }}>
|
||||||
{({ opacity, height }) =>
|
{({ opacity, height }) =>
|
||||||
<label style={{ display: 'block', lineHeight: '24px', verticalAlign: 'middle', height: `${height}px`, overflow: 'hidden', opacity: opacity / 100 }}>
|
<label style={{ display: 'block', lineHeight: '24px', verticalAlign: 'middle', height: `${height}px`, overflow: 'hidden', opacity: opacity / 100 }}>
|
||||||
<Toggle checked={this.props.unlisted} onChange={this.handleChangeListability} />
|
<Toggle checked={this.props.unlisted} onChange={this.handleChangeListability} />
|
||||||
|
@ -138,7 +139,7 @@ const ComposeForm = React.createClass({
|
||||||
}
|
}
|
||||||
</Motion>
|
</Motion>
|
||||||
|
|
||||||
<Motion defaultStyle={{ opacity: 100, height: 39.5 }} style={{ opacity: spring(this.props.media_count === 0 ? 0 : 100), height: spring(this.props.media_count === 0 ? 0 : 39.5) }}>
|
<Motion defaultStyle={{ opacity: 0, height: 0 }} style={{ opacity: spring(this.props.media_count === 0 ? 0 : 100), height: spring(this.props.media_count === 0 ? 0 : 39.5) }}>
|
||||||
{({ opacity, height }) =>
|
{({ opacity, height }) =>
|
||||||
<label style={{ display: 'block', lineHeight: '24px', verticalAlign: 'middle', height: `${height}px`, overflow: 'hidden', opacity: opacity / 100 }}>
|
<label style={{ display: 'block', lineHeight: '24px', verticalAlign: 'middle', height: `${height}px`, overflow: 'hidden', opacity: opacity / 100 }}>
|
||||||
<Toggle checked={this.props.sensitive} onChange={this.handleChangeSensitivity} />
|
<Toggle checked={this.props.sensitive} onChange={this.handleChangeSensitivity} />
|
||||||
|
|
|
@ -35,7 +35,7 @@ const AccountAuthorize = ({ intl, account, onAuthorize, onReject }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div style={outerStyle}>
|
<div style={outerStyle}>
|
||||||
<Permalink href={account.get('url')} className='detailed-status__display-name' style={{ display: 'block', overflow: 'hidden', marginBottom: '15px' }}>
|
<Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name' style={{ display: 'block', overflow: 'hidden', marginBottom: '15px' }}>
|
||||||
<div style={{ float: 'left', marginRight: '10px' }}><Avatar src={account.get('avatar')} size={48} /></div>
|
<div style={{ float: 'left', marginRight: '10px' }}><Avatar src={account.get('avatar')} size={48} /></div>
|
||||||
<DisplayName account={account} />
|
<DisplayName account={account} />
|
||||||
</Permalink>
|
</Permalink>
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Api::V1::NotificationsController < ApiController
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@notifications = Notification.where(account: current_account).paginate_by_max_id(20, params[:max_id], params[:since_id])
|
@notifications = Notification.where(account: current_account).browserable.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||||
@notifications = cache_collection(@notifications, Notification)
|
@notifications = cache_collection(@notifications, Notification)
|
||||||
statuses = @notifications.select { |n| !n.target_status.nil? }.map(&:target_status)
|
statuses = @notifications.select { |n| !n.target_status.nil? }.map(&:target_status)
|
||||||
|
|
||||||
|
|
|
@ -40,4 +40,13 @@ class NotificationMailer < ApplicationMailer
|
||||||
mail to: @me.user.email, subject: I18n.t('notification_mailer.reblog.subject', name: @account.acct)
|
mail to: @me.user.email, subject: I18n.t('notification_mailer.reblog.subject', name: @account.acct)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def follow_request(recipient, notification)
|
||||||
|
@me = recipient
|
||||||
|
@account = notification.from_account
|
||||||
|
|
||||||
|
I18n.with_locale(@me.user.locale || I18n.default_locale) do
|
||||||
|
mail to: @me.user.email, subject: I18n.t('notification_mailer.follow_request.subject', name: @account.acct)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,8 @@ class FollowRequest < ApplicationRecord
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
belongs_to :target_account, class_name: 'Account'
|
belongs_to :target_account, class_name: 'Account'
|
||||||
|
|
||||||
|
has_one :notification, as: :activity, dependent: :destroy
|
||||||
|
|
||||||
validates :account, :target_account, presence: true
|
validates :account, :target_account, presence: true
|
||||||
validates :account_id, uniqueness: { scope: :target_account_id }
|
validates :account_id, uniqueness: { scope: :target_account_id }
|
||||||
|
|
||||||
|
|
|
@ -8,16 +8,18 @@ class Notification < ApplicationRecord
|
||||||
belongs_to :from_account, class_name: 'Account'
|
belongs_to :from_account, class_name: 'Account'
|
||||||
belongs_to :activity, polymorphic: true
|
belongs_to :activity, polymorphic: true
|
||||||
|
|
||||||
belongs_to :mention, foreign_type: 'Mention', foreign_key: 'activity_id'
|
belongs_to :mention, foreign_type: 'Mention', foreign_key: 'activity_id'
|
||||||
belongs_to :status, foreign_type: 'Status', foreign_key: 'activity_id'
|
belongs_to :status, foreign_type: 'Status', foreign_key: 'activity_id'
|
||||||
belongs_to :follow, foreign_type: 'Follow', foreign_key: 'activity_id'
|
belongs_to :follow, foreign_type: 'Follow', foreign_key: 'activity_id'
|
||||||
belongs_to :favourite, foreign_type: 'Favourite', foreign_key: 'activity_id'
|
belongs_to :follow_request, foreign_type: 'FollowRequest', foreign_key: 'activity_id'
|
||||||
|
belongs_to :favourite, foreign_type: 'Favourite', foreign_key: 'activity_id'
|
||||||
|
|
||||||
validates :account_id, uniqueness: { scope: [:activity_type, :activity_id] }
|
validates :account_id, uniqueness: { scope: [:activity_type, :activity_id] }
|
||||||
|
|
||||||
STATUS_INCLUDES = [:account, :stream_entry, :media_attachments, :tags, mentions: :account, reblog: [:stream_entry, :account, :media_attachments, :tags, mentions: :account]].freeze
|
STATUS_INCLUDES = [:account, :stream_entry, :media_attachments, :tags, mentions: :account, reblog: [:stream_entry, :account, :media_attachments, :tags, mentions: :account]].freeze
|
||||||
|
|
||||||
scope :cache_ids, -> { select(:id, :updated_at, :activity_type, :activity_id) }
|
scope :cache_ids, -> { select(:id, :updated_at, :activity_type, :activity_id) }
|
||||||
|
scope :browserable, -> { where.not(activity_type: ['FollowRequest']) }
|
||||||
|
|
||||||
cache_associated :from_account, status: STATUS_INCLUDES, mention: [status: STATUS_INCLUDES], favourite: [:account, status: STATUS_INCLUDES], follow: :account
|
cache_associated :from_account, status: STATUS_INCLUDES, mention: [status: STATUS_INCLUDES], favourite: [:account, status: STATUS_INCLUDES], follow: :account
|
||||||
|
|
||||||
|
@ -30,7 +32,7 @@ class Notification < ApplicationRecord
|
||||||
when 'Status'
|
when 'Status'
|
||||||
:reblog
|
:reblog
|
||||||
else
|
else
|
||||||
activity_type.downcase.to_sym
|
activity_type.underscore.to_sym
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,6 +45,10 @@ class Notification < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def browserable?
|
||||||
|
type != :follow_request
|
||||||
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def reload_stale_associations!(cached_items)
|
def reload_stale_associations!(cached_items)
|
||||||
account_ids = cached_items.map(&:from_account_id).uniq
|
account_ids = cached_items.map(&:from_account_id).uniq
|
||||||
|
@ -61,7 +67,7 @@ class Notification < ApplicationRecord
|
||||||
|
|
||||||
def set_from_account
|
def set_from_account
|
||||||
case activity_type
|
case activity_type
|
||||||
when 'Status', 'Follow', 'Favourite'
|
when 'Status', 'Follow', 'Favourite', 'FollowRequest'
|
||||||
self.from_account_id = activity(false)&.account_id
|
self.from_account_id = activity(false)&.account_id
|
||||||
when 'Mention'
|
when 'Mention'
|
||||||
self.from_account_id = activity(false)&.status&.account_id
|
self.from_account_id = activity(false)&.status&.account_id
|
||||||
|
|
|
@ -15,7 +15,7 @@ class User < ApplicationRecord
|
||||||
scope :admins, -> { where(admin: true) }
|
scope :admins, -> { where(admin: true) }
|
||||||
|
|
||||||
has_settings do |s|
|
has_settings do |s|
|
||||||
s.key :notification_emails, defaults: { follow: false, reblog: false, favourite: false, mention: false }
|
s.key :notification_emails, defaults: { follow: false, reblog: false, favourite: false, mention: false, follow_request: true }
|
||||||
s.key :interactions, defaults: { must_be_follower: false, must_be_following: false }
|
s.key :interactions, defaults: { must_be_follower: false, must_be_following: false }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,12 @@ class FollowService < BaseService
|
||||||
private
|
private
|
||||||
|
|
||||||
def request_follow(source_account, target_account)
|
def request_follow(source_account, target_account)
|
||||||
FollowRequest.create!(account: source_account, target_account: target_account)
|
return unless target_account.local?
|
||||||
|
|
||||||
|
follow_request = FollowRequest.create!(account: source_account, target_account: target_account)
|
||||||
|
NotifyService.new.call(target_account, follow_request)
|
||||||
|
|
||||||
|
follow_request
|
||||||
end
|
end
|
||||||
|
|
||||||
def direct_follow(source_account, target_account)
|
def direct_follow(source_account, target_account)
|
||||||
|
|
|
@ -32,6 +32,10 @@ class NotifyService < BaseService
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def blocked_follow_request?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def blocked?
|
def blocked?
|
||||||
blocked = @recipient.suspended? # Skip if the recipient account is suspended anyway
|
blocked = @recipient.suspended? # Skip if the recipient account is suspended anyway
|
||||||
blocked ||= @recipient.id == @notification.from_account.id # Skip for interactions with self
|
blocked ||= @recipient.id == @notification.from_account.id # Skip for interactions with self
|
||||||
|
@ -45,6 +49,7 @@ class NotifyService < BaseService
|
||||||
|
|
||||||
def create_notification
|
def create_notification
|
||||||
@notification.save!
|
@notification.save!
|
||||||
|
return unless @notification.browserable?
|
||||||
FeedManager.instance.broadcast(@recipient.id, type: 'notification', message: FeedManager.instance.inline_render(@recipient, 'api/v1/notifications/show', @notification))
|
FeedManager.instance.broadcast(@recipient.id, type: 'notification', message: FeedManager.instance.inline_render(@recipient, 'api/v1/notifications/show', @notification))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<%= display_name(@me) %>,
|
||||||
|
|
||||||
|
<%= t('notification_mailer.follow_request.body', name: @account.acct) %>
|
||||||
|
|
||||||
|
<%= web_url("follow_requests") %>
|
|
@ -54,6 +54,9 @@ en:
|
||||||
follow:
|
follow:
|
||||||
body: "%{name} is now following you!"
|
body: "%{name} is now following you!"
|
||||||
subject: "%{name} is now following you"
|
subject: "%{name} is now following you"
|
||||||
|
follow_request:
|
||||||
|
body: "%{name} has requested to follow you"
|
||||||
|
subject: 'Pending follower: %{name}'
|
||||||
mention:
|
mention:
|
||||||
body: 'You were mentioned by %{name} in:'
|
body: 'You were mentioned by %{name} in:'
|
||||||
subject: You were mentioned by %{name}
|
subject: You were mentioned by %{name}
|
||||||
|
|
Reference in New Issue