Skip to content

Commit

Permalink
Merge pull request mastodon#1663 from ClearlyClaire/glitch-soc/merge-…
Browse files Browse the repository at this point in the history
…upstream

Merge upstream changes
  • Loading branch information
ClearlyClaire authored Jan 23, 2022
2 parents b209e91 + 9483d0c commit e58e0eb
Show file tree
Hide file tree
Showing 94 changed files with 1,767 additions and 813 deletions.
4 changes: 0 additions & 4 deletions .env.nanobox
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,6 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)
# PAM_CONTROLLED_SERVICE=rpam

# Global OAuth settings (optional) :
# If you have only one strategy, you may want to enable this
# OAUTH_REDIRECT_AT_SIGN_IN=true

# Optional CAS authentication (cf. omniauth-cas) :
# CAS_ENABLED=true
# CAS_URL=https://sso.myserver.com/
Expand Down
6 changes: 5 additions & 1 deletion app/controllers/api/v1/accounts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,14 @@ def account_params
end

def check_enabled_registrations
forbidden if single_user_mode? || !allowed_registrations?
forbidden if single_user_mode? || omniauth_only? || !allowed_registrations?
end

def allowed_registrations?
Setting.registrations_mode != 'none'
end

def omniauth_only?
ENV['OMNIAUTH_ONLY'] == 'true'
end
end
21 changes: 21 additions & 0 deletions app/controllers/api/v1/statuses/histories_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

class Api::V1::Statuses::HistoriesController < Api::BaseController
include Authorization

before_action -> { authorize_if_got_token! :read, :'read:statuses' }
before_action :set_status

def show
render json: @status.edits, each_serializer: REST::StatusEditSerializer
end

private

def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
end
21 changes: 21 additions & 0 deletions app/controllers/api/v1/statuses/sources_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

class Api::V1::Statuses::SourcesController < Api::BaseController
include Authorization

before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
before_action :set_status

def show
render json: @status, serializer: REST::StatusSourceSerializer
end

private

def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
end
6 changes: 5 additions & 1 deletion app/controllers/auth/registrations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,17 @@ def after_update_path_for(_resource)
end

def check_enabled_registrations
redirect_to root_path if single_user_mode? || !allowed_registrations?
redirect_to root_path if single_user_mode? || omniauth_only? || !allowed_registrations?
end

def allowed_registrations?
Setting.registrations_mode != 'none' || @invite&.valid_for_use?
end

def omniauth_only?
ENV['OMNIAUTH_ONLY'] == 'true'
end

def invite_code
if params[:user]
params[:user][:invite_code]
Expand Down
16 changes: 0 additions & 16 deletions app/controllers/auth/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,6 @@ class Auth::SessionsController < Devise::SessionsController
before_action :set_instance_presenter, only: [:new]
before_action :set_body_classes

def new
Devise.omniauth_configs.each do |provider, config|
return redirect_to(omniauth_authorize_path(resource_name, provider)) if config.strategy.redirect_at_sign_in
end

super
end

def create
super do |resource|
# We only need to call this if this hasn't already been
Expand Down Expand Up @@ -89,14 +81,6 @@ def after_sign_in_path_for(resource)
end
end

def after_sign_out_path_for(_resource_or_scope)
Devise.omniauth_configs.each_value do |config|
return root_path if config.strategy.redirect_at_sign_in
end

super
end

def require_no_authentication
super

Expand Down
26 changes: 25 additions & 1 deletion app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,37 @@ def closed_registrations?
end

def available_sign_up_path
if closed_registrations?
if closed_registrations? || omniauth_only?
'https://joinmastodon.org/#getting-started'
else
new_user_registration_path
end
end

def omniauth_only?
ENV['OMNIAUTH_ONLY'] == 'true'
end

def link_to_login(name = nil, html_options = nil, &block)
target = new_user_session_path

if omniauth_only? && Devise.mappings[:user].omniauthable? && User.omniauth_providers.size == 1
target = omniauth_authorize_path(:user, User.omniauth_providers[0])
html_options ||= {}
html_options[:method] = :post
end

if block_given?
link_to(target, html_options, &block)
else
link_to(name, target, html_options)
end
end

def provider_sign_in_link(provider)
link_to I18n.t("auth.providers.#{provider}", default: provider.to_s.chomp('_oauth2').capitalize), omniauth_authorize_path(:user, provider), class: "button button-#{provider}", method: :post
end

def open_deletion?
Setting.open_deletion
end
Expand Down
8 changes: 7 additions & 1 deletion app/helpers/jsonld_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ def url_to_href(value, preferred_type = nil)
end

def as_array(value)
value.is_a?(Array) ? value : [value]
if value.nil?
[]
elsif value.is_a?(Array)
value
else
[value]
end
end

def value_or_id(value)
Expand Down
7 changes: 4 additions & 3 deletions app/javascript/flavours/glitch/actions/importer/normalizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ export function normalizeStatus(status, normalOldStatus) {
normalStatus.poll = status.poll.id;
}

// Only calculate these values when status first encountered
// Otherwise keep the ones already in the reducer
if (normalOldStatus) {
// Only calculate these values when status first encountered and
// when the underlying values change. Otherwise keep the ones
// already in the reducer
if (normalOldStatus && normalOldStatus.get('content') === normalStatus.content && normalOldStatus.get('spoiler_text') === normalStatus.spoiler_text) {
normalStatus.search_index = normalOldStatus.get('search_index');
normalStatus.contentHtml = normalOldStatus.get('contentHtml');
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');
Expand Down
3 changes: 3 additions & 0 deletions app/javascript/flavours/glitch/actions/statuses.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ export function deleteStatusFail(id, error) {
};
};

export const updateStatus = status => dispatch =>
dispatch(importFetchedStatus(status));

export function fetchContext(id) {
return (dispatch, getState) => {
dispatch(fetchContextRequest(id));
Expand Down
4 changes: 4 additions & 0 deletions app/javascript/flavours/glitch/actions/streaming.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from './timelines';
import { updateNotifications, expandNotifications } from './notifications';
import { updateConversations } from './conversations';
import { updateStatus } from './statuses';
import {
fetchAnnouncements,
updateAnnouncements,
Expand Down Expand Up @@ -75,6 +76,9 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
case 'update':
dispatch(updateTimeline(timelineId, JSON.parse(data.payload), options.accept));
break;
case 'status.update':
dispatch(updateStatus(JSON.parse(data.payload)));
break;
case 'delete':
dispatch(deleteFromTimelines(data.payload));
break;
Expand Down
6 changes: 3 additions & 3 deletions app/javascript/flavours/glitch/components/admin/Retention.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default class Retention extends React.PureComponent {
</td>

{data[0].data.slice(1).map((retention, i) => {
const average = data.reduce((sum, cohort, k) => cohort.data[i + 1] ? sum + (cohort.data[i + 1].percent - sum)/(k + 1) : sum, 0);
const average = data.reduce((sum, cohort, k) => cohort.data[i + 1] ? sum + (cohort.data[i + 1].rate - sum)/(k + 1) : sum, 0);

return (
<td key={retention.date}>
Expand Down Expand Up @@ -118,8 +118,8 @@ export default class Retention extends React.PureComponent {

{cohort.data.slice(1).map(retention => (
<td key={retention.date}>
<div className={classNames('retention__table__box', `retention__table__box--${roundTo10(retention.percent * 100)}`)}>
<FormattedNumber value={retention.percent} style='percent' />
<div className={classNames('retention__table__box', `retention__table__box--${roundTo10(retention.rate * 100)}`)}>
<FormattedNumber value={retention.rate} style='percent' />
</div>
</td>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const messages = defineMessages({
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
hide: { id: 'status.hide', defaultMessage: 'Hide toot' },
edited: { id: 'status.edited', defaultMessage: 'Edited {date}' },
});

export default @injectIntl
Expand Down Expand Up @@ -324,7 +325,9 @@ class StatusActionBar extends ImmutablePureComponent {
</div>,
]}

<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'>
<RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
</a>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class ComposeForm extends ImmutablePureComponent {
onPickEmoji: PropTypes.func,
showSearch: PropTypes.bool,
anyMedia: PropTypes.bool,
isInReply: PropTypes.bool,
singleColumn: PropTypes.bool,

advancedOptions: ImmutablePropTypes.map,
Expand Down Expand Up @@ -233,7 +234,7 @@ class ComposeForm extends ImmutablePureComponent {
// Caret/selection handling.
if (focusDate !== prevProps.focusDate) {
switch (true) {
case preselectDate !== prevProps.preselectDate && preselectOnReply:
case preselectDate !== prevProps.preselectDate && this.props.isInReply && preselectOnReply:
selectionStart = text.search(/\s/) + 1;
selectionEnd = text.length;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ function mapStateToProps (state) {
spoilersAlwaysOn: spoilersAlwaysOn,
mediaDescriptionConfirmation: state.getIn(['local_settings', 'confirm_missing_media_description']),
preselectOnReply: state.getIn(['local_settings', 'preselect_on_reply']),
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import StatusContent from 'flavours/glitch/components/status_content';
import MediaGallery from 'flavours/glitch/components/media_gallery';
import AttachmentList from 'flavours/glitch/components/attachment_list';
import { Link } from 'react-router-dom';
import { FormattedDate } from 'react-intl';
import { injectIntl, FormattedDate, FormattedMessage } from 'react-intl';
import Card from './card';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Video from 'flavours/glitch/features/video';
Expand All @@ -20,7 +20,8 @@ import Icon from 'flavours/glitch/components/icon';
import AnimatedNumber from 'flavours/glitch/components/animated_number';
import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';

export default class DetailedStatus extends ImmutablePureComponent {
export default @injectIntl
class DetailedStatus extends ImmutablePureComponent {

static contextTypes = {
router: PropTypes.object,
Expand All @@ -40,6 +41,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
showMedia: PropTypes.bool,
usingPiP: PropTypes.bool,
onToggleMediaVisibility: PropTypes.func,
intl: PropTypes.object.isRequired,
};

state = {
Expand Down Expand Up @@ -111,7 +113,7 @@ export default class DetailedStatus extends ImmutablePureComponent {

render () {
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
const { expanded, onToggleHidden, settings, usingPiP } = this.props;
const { expanded, onToggleHidden, settings, usingPiP, intl } = this.props;
const outerStyle = { boxSizing: 'border-box' };
const { compact } = this.props;

Expand All @@ -125,6 +127,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
let reblogLink = '';
let reblogIcon = 'retweet';
let favouriteLink = '';
let edited = '';

if (this.props.measureHeight) {
outerStyle.height = `${this.state.height}px`;
Expand Down Expand Up @@ -258,6 +261,15 @@ export default class DetailedStatus extends ImmutablePureComponent {
);
}

if (status.get('edited_at')) {
edited = (
<React.Fragment>
<React.Fragment> · </React.Fragment>
<FormattedMessage id='status.edited' defaultMessage='Edited {date}' values={{ date: intl.formatDate(status.get('edited_at'), { hour12: false, month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) }} />
</React.Fragment>
);
}

return (
<div style={outerStyle}>
<div ref={this.setRef} className={classNames('detailed-status', `detailed-status-${status.get('visibility')}`, { compact })} data-status-by={status.getIn(['account', 'acct'])}>
Expand All @@ -283,7 +295,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
<div className='detailed-status__meta'>
<a className='detailed-status__datetime' href={status.get('url')} target='_blank' rel='noopener noreferrer'>
<FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />
</a>{visibilityLink}{applicationLink}{reblogLink} · {favouriteLink}
</a>{edited}{visibilityLink}{applicationLink}{reblogLink} · {favouriteLink}
</div>
</div>
</div>
Expand Down
11 changes: 11 additions & 0 deletions app/javascript/flavours/glitch/styles/components/status.scss
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,17 @@
}
}

.status__content__edited-label {
display: block;
cursor: default;
font-size: 15px;
line-height: 20px;
padding: 0;
padding-top: 8px;
color: $dark-text-color;
font-weight: 500;
}

.status__content__spoiler-link {
display: inline-block;
border-radius: 2px;
Expand Down
7 changes: 4 additions & 3 deletions app/javascript/mastodon/actions/importer/normalizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ export function normalizeStatus(status, normalOldStatus) {
normalStatus.poll = status.poll.id;
}

// Only calculate these values when status first encountered
// Otherwise keep the ones already in the reducer
if (normalOldStatus) {
// Only calculate these values when status first encountered and
// when the underlying values change. Otherwise keep the ones
// already in the reducer
if (normalOldStatus && normalOldStatus.get('content') === normalStatus.content && normalOldStatus.get('spoiler_text') === normalStatus.spoiler_text) {
normalStatus.search_index = normalOldStatus.get('search_index');
normalStatus.contentHtml = normalOldStatus.get('contentHtml');
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');
Expand Down
3 changes: 3 additions & 0 deletions app/javascript/mastodon/actions/statuses.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ export function deleteStatusFail(id, error) {
};
};

export const updateStatus = status => dispatch =>
dispatch(importFetchedStatus(status));

export function fetchContext(id) {
return (dispatch, getState) => {
dispatch(fetchContextRequest(id));
Expand Down
Loading

0 comments on commit e58e0eb

Please sign in to comment.