Skip to content

Commit

Permalink
Merge remote-tracking branch 'parent/main' into upstream-20231116
Browse files Browse the repository at this point in the history
  • Loading branch information
kmycode committed Nov 16, 2023
2 parents 9ba8d8f + c94bedf commit 236fc2a
Show file tree
Hide file tree
Showing 162 changed files with 1,916 additions and 1,205 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ services:
hard: -1

libretranslate:
image: libretranslate/libretranslate:v1.4.1
image: libretranslate/libretranslate:v1.5.2
restart: unless-stopped
volumes:
- lt-data:/home/libretranslate/.local
Expand Down
13 changes: 13 additions & 0 deletions .github/codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
coverage:
status:
project:
default:
# Github status check is not blocking
informational: true
patch:
default:
# Github status check is not blocking
informational: true
comment:
# Only write a comment in PR if there are changes
require_changes: true
22 changes: 22 additions & 0 deletions .simplecov
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

if ENV['CI']
require 'simplecov-lcov'
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
else
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
end

SimpleCov.start 'rails' do
enable_coverage :branch

add_filter 'lib/linter'

add_group 'Libraries', 'lib'
add_group 'Policies', 'app/policies'
add_group 'Presenters', 'app/presenters'
add_group 'Serializers', 'app/serializers'
add_group 'Services', 'app/services'
add_group 'Validators', 'app/validators'
end
2 changes: 2 additions & 0 deletions app/controllers/api/v1/accounts/credentials_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ def update
current_user.update(user_params) if user_params
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
render json: @account, serializer: REST::CredentialAccountSerializer
rescue ActiveRecord::RecordInvalid => e
render json: ValidationErrorFormatter.new(e).as_json, status: 422
end

private
Expand Down
74 changes: 48 additions & 26 deletions app/controllers/api/web/push_subscriptions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,56 @@
class Api::Web::PushSubscriptionsController < Api::Web::BaseController
before_action :require_user!
before_action :set_push_subscription, only: :update
before_action :destroy_previous_subscriptions, only: :create, if: :prior_subscriptions?
after_action :update_session_with_subscription, only: :create

def create
active_session = current_session
@push_subscription = ::Web::PushSubscription.create!(web_push_subscription_params)

unless active_session.web_push_subscription.nil?
active_session.web_push_subscription.destroy!
active_session.update!(web_push_subscription: nil)
end
render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
end

# Mobile devices do not support regular notifications, so we enable push notifications by default
alerts_enabled = active_session.detection.device.mobile? || active_session.detection.device.tablet?
def update
@push_subscription.update!(data: data_params)
render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
end

data = {
policy: 'all',
alerts: Notification::TYPES.index_with { alerts_enabled },
}
private

data.deep_merge!(data_params) if params[:data]
def active_session
@active_session ||= current_session
end

push_subscription = ::Web::PushSubscription.create!(
endpoint: subscription_params[:endpoint],
key_p256dh: subscription_params[:keys][:p256dh],
key_auth: subscription_params[:keys][:auth],
data: data,
user_id: active_session.user_id,
access_token_id: active_session.access_token_id
)
def destroy_previous_subscriptions
active_session.web_push_subscription.destroy!
active_session.update!(web_push_subscription: nil)
end

def prior_subscriptions?
active_session.web_push_subscription.present?
end

active_session.update!(web_push_subscription: push_subscription)
def subscription_data
default_subscription_data.tap do |data|
data.deep_merge!(data_params) if params[:data]
end
end

render json: push_subscription, serializer: REST::WebPushSubscriptionSerializer
def default_subscription_data
{
policy: 'all',
alerts: Notification::TYPES.index_with { alerts_enabled },
}
end

def update
@push_subscription.update!(data: data_params)
render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
def alerts_enabled
# Mobile devices do not support regular notifications, so we enable push notifications by default
active_session.detection.device.mobile? || active_session.detection.device.tablet?
end

private
def update_session_with_subscription
active_session.update!(web_push_subscription: @push_subscription)
end

def set_push_subscription
@push_subscription = ::Web::PushSubscription.find(params[:id])
Expand All @@ -51,6 +62,17 @@ def subscription_params
@subscription_params ||= params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
end

def web_push_subscription_params
{
access_token_id: active_session.access_token_id,
data: subscription_data,
endpoint: subscription_params[:endpoint],
key_auth: subscription_params[:keys][:auth],
key_p256dh: subscription_params[:keys][:p256dh],
user_id: active_session.user_id,
}
end

def data_params
@data_params ||= params.require(:data).permit(:policy, alerts: Notification::TYPES)
end
Expand Down
15 changes: 15 additions & 0 deletions app/javascript/mastodon/actions/accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -661,3 +661,18 @@ export function unpinAccountFail(error) {
error,
};
}

export const updateAccount = ({ displayName, note, avatar, header, discoverable, indexable }) => (dispatch, getState) => {
const data = new FormData();

data.append('display_name', displayName);
data.append('note', note);
if (avatar) data.append('avatar', avatar);
if (header) data.append('header', header);
data.append('discoverable', discoverable);
data.append('indexable', indexable);

return api(getState).patch('/api/v1/accounts/update_credentials', data).then(response => {
dispatch(importFetchedAccount(response.data));
});
};
1 change: 1 addition & 0 deletions app/javascript/mastodon/api_types/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface ApiAccountJSON {
bot: boolean;
created_at: string;
discoverable: boolean;
indexable: boolean;
display_name: string;
emojis: ApiCustomEmojiJSON[];
fields: ApiAccountFieldJSON[];
Expand Down
2 changes: 1 addition & 1 deletion app/javascript/mastodon/components/admin/Retention.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default class Retention extends PureComponent {
let content;

if (loading) {
content = <FormattedMessage id='loading_indicator.label' defaultMessage='Loading...' />;
content = <FormattedMessage id='loading_indicator.label' defaultMessage='Loading' />;
} else {
content = (
<table className='retention__table'>
Expand Down
44 changes: 44 additions & 0 deletions app/javascript/mastodon/components/copy_icon_button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import PropTypes from 'prop-types';
import { useState, useCallback } from 'react';

import { defineMessages } from 'react-intl';

import classNames from 'classnames';

import { useDispatch } from 'react-redux';

import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg';

import { showAlert } from 'mastodon/actions/alerts';
import { IconButton } from 'mastodon/components/icon_button';

const messages = defineMessages({
copied: { id: 'copy_icon_button.copied', defaultMessage: 'Copied to clipboard' },
});

export const CopyIconButton = ({ title, value, className }) => {
const [copied, setCopied] = useState(false);
const dispatch = useDispatch();

const handleClick = useCallback(() => {
navigator.clipboard.writeText(value);
setCopied(true);
dispatch(showAlert({ message: messages.copied }));
setTimeout(() => setCopied(false), 700);
}, [setCopied, value, dispatch]);

return (
<IconButton
className={classNames(className, copied ? 'copied' : 'copyable')}
title={title}
onClick={handleClick}
iconComponent={ContentCopyIcon}
/>
);
};

CopyIconButton.propTypes = {
title: PropTypes.string,
value: PropTypes.string,
className: PropTypes.string,
};
26 changes: 21 additions & 5 deletions app/javascript/mastodon/components/loading_indicator.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import { useIntl, defineMessages } from 'react-intl';

import { CircularProgress } from './circular_progress';

export const LoadingIndicator: React.FC = () => (
<div className='loading-indicator'>
<CircularProgress size={50} strokeWidth={6} />
</div>
);
const messages = defineMessages({
loading: { id: 'loading_indicator.label', defaultMessage: 'Loading…' },
});

export const LoadingIndicator: React.FC = () => {
const intl = useIntl();

return (
<div
className='loading-indicator'
role='progressbar'
aria-busy
aria-live='polite'
aria-label={intl.formatMessage(messages.loading)}
>
<CircularProgress size={50} strokeWidth={6} />
</div>
);
};
23 changes: 14 additions & 9 deletions app/javascript/mastodon/features/account/components/header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/l
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
import { ReactComponent as NotificationsIcon } from '@material-symbols/svg-600/outlined/notifications.svg';
import { ReactComponent as NotificationsActiveIcon } from '@material-symbols/svg-600/outlined/notifications_active-fill.svg';
import { ReactComponent as ShareIcon } from '@material-symbols/svg-600/outlined/share.svg';

import { Avatar } from 'mastodon/components/avatar';
import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge';
import { Button } from 'mastodon/components/button';
import { CopyIconButton } from 'mastodon/components/copy_icon_button';
import { FollowersCounter, FollowingCounter, StatusesCounter } from 'mastodon/components/counters';
import { Icon } from 'mastodon/components/icon';
import { IconButton } from 'mastodon/components/icon_button';
Expand Down Expand Up @@ -46,6 +48,7 @@ const messages = defineMessages({
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
report: { id: 'account.report', defaultMessage: 'Report @{name}' },
share: { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' },
copy: { id: 'account.copy', defaultMessage: 'Copy link to profile' },
media: { id: 'account.media', defaultMessage: 'Media' },
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
Expand Down Expand Up @@ -251,11 +254,10 @@ class Header extends ImmutablePureComponent {
const isRemote = account.get('acct') !== account.get('username');
const remoteDomain = isRemote ? account.get('acct').split('@')[1] : null;

let info = [];
let actionBtn = '';
let bellBtn = '';
let lockedIcon = '';
let menu = [];
let actionBtn, bellBtn, lockedIcon, shareBtn;

let info = [];
let menu = [];

if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
info.push(<span key='followed_by' className='relationship-tag'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>);
Expand All @@ -273,6 +275,12 @@ class Header extends ImmutablePureComponent {
bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} iconComponent={account.getIn(['relationship', 'notifying']) ? NotificationsActiveIcon : NotificationsIcon} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
}

if ('share' in navigator) {
shareBtn = <IconButton className='optional' iconComponent={ShareIcon} title={intl.formatMessage(messages.share, { name: account.get('username') })} onClick={this.handleShare} />;
} else {
shareBtn = <CopyIconButton className='optional' title={intl.formatMessage(messages.copy)} value={account.get('url')} />;
}

if (me !== account.get('id')) {
if (signedIn && !account.get('relationship')) { // Wait until the relationship is loaded
actionBtn = '';
Expand Down Expand Up @@ -303,10 +311,6 @@ class Header extends ImmutablePureComponent {

if (isRemote) {
menu.push({ text: intl.formatMessage(messages.openOriginalPage), href: account.get('url') });
}

if ('share' in navigator && !account.get('suspended')) {
menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
menu.push(null);
}

Expand Down Expand Up @@ -425,6 +429,7 @@ class Header extends ImmutablePureComponent {
<>
{actionBtn}
{bellBtn}
{shareBtn}
</>
)}

Expand Down

This file was deleted.

Loading

0 comments on commit 236fc2a

Please sign in to comment.